library(tidyverse)
library(sf)
library(raster)
library(knitr)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
#library(QuantPsyc) # JE Note: in R 4.1, QuantPsyc package not available.
library(caret)
library(yardstick)
library(pscl)
library(plotROC)
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)
plotTheme <- theme(
plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
axis.text.y = element_text(size = 10),
axis.title.y = element_text(size = 10),
# Set the entire chart region to blank
panel.background=element_blank(),
plot.background=element_blank(),
#panel.border=element_rect(colour="#F0F0F0"),
# Format the grid
panel.grid.major=element_line(colour="#D0D0D0",size=.75),
axis.ticks=element_blank())
mapTheme <- theme(plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_line(colour = 'transparent'),
panel.grid.minor=element_blank(),
legend.direction = "vertical",
legend.position = "right",
plot.margin = margin(1, 1, 1, 1, 'cm'),
legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))
palette2 <- c("#41b6c4","#253494")
palette4 <- c("#a1dab4","#41b6c4","#2c7fb8","#253494")
palette5 <- c("#ffffcc","#a1dab4","#41b6c4","#2c7fb8","#253494")
palette10 <- c("#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4",
"#4eb3d3","#2b8cbe","#0868ac","#084081","#f7fcf0")
#this function converts a column in to quintiles. It is used for mapping.
quintileBreaks <- function(df,variable) {
as.character(quantile(df[[variable]],
c(.01,.2,.4,.6,.8),na.rm=T))
}
#This function can be used to convert a polygon sf to centroids xy coords.
xyC <- function(aPolygonSF) {
as.data.frame(
cbind(x=st_coordinates(st_centroid(aPolygonSF))[,1],
y=st_coordinates(st_centroid(aPolygonSF))[,2]))
}
#this function convert a raster to a data frame so it can be plotted in ggplot
rast <- function(inRaster) {
data.frame(
xyFromCell(inRaster, 1:ncell(inRaster)),
value = getValues(inRaster)) }
1: Set up
dvMSA <-
st_read("/Users/luyiiwong/Documents/Land Use & Environmental Modeling/Assignment5/dv_counties/dv_counties.shp")
lc_2011 = raster("/Users/luyiiwong/Documents/Land Use & Environmental Modeling/Assignment5/lc_2011_clip_Resample.tif")
lc_2021 = raster("/Users/luyiiwong/Documents/Land Use & Environmental Modeling/Assignment5/lc_2021_clip_Resample.tif")
lc_change <- lc_2011+lc_2021
2: Calculating Land Cover Change
#creating a matrix to classify urban and non-urban land use
reclassMatrix <-
matrix(c(
0,12,0,
12,24,1,
24,Inf,0),
ncol=3, byrow=T)
# reclassifying land cover for both years
developed_2011 <-
reclassify(lc_2011,reclassMatrix)
developed_2021 <-
reclassify(lc_2021,reclassMatrix)
# calculating land use change from 2011 to 2021
development_change <- developed_2011+developed_2021
# the values that are =1 indicate that there was CHANGE between 2011 and 2021
# histogram shows that there were very few values in the study area
hist(development_change)

# reclassifying values that are not equal to 1 to NA in development change
# this means that values 0 and 2 become NA
development_change[development_change != 1] <- NA
# plotting the values of development change = 1 spatially
ggplot() +
geom_sf(data=dvMSA) +
geom_raster(data=rast(development_change) %>% na.omit,
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE, name ="Land Cover\nChange") +
labs(title="Development land use change") +
mapTheme

2.1: Creating the fishnet
dvMSA_fishnet <-
st_make_grid(dvMSA, 500) %>%
st_sf()
dvMSA_fishnet <-
dvMSA_fishnet[dvMSA,]
ggplot() +
geom_sf(data=dvMSA_fishnet) +
labs(title="Fishnet, 500 Foot Resolution") +
mapTheme

#original version
changePoints <-
rasterToPoints(development_change) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(dvMSA_fishnet))
# changed this naming from lc_change to layer then converted NA to 0
# because in fishnet here, it's called layer not lc_change
fishnet <-
aggregate(changePoints, dvMSA_fishnet, sum) %>%
mutate(layer = ifelse(is.na(layer),0,1),
layer = as.factor(layer))
ggplot() +
geom_sf(data=dvMSA) +
geom_point(data=fishnet,
aes(x=xyC(fishnet)$x, y=xyC(fishnet)$y, colour=layer)) +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development"),
name = "") +
labs(title = "Land Cover Development Change", subtitle = "As fishnet centroids") +
mapTheme

2.2: Plotting Land Cover in 2011
ggplot() +
geom_sf(data=dvMSA) +
geom_raster(data=rast(lc_2011) %>% na.omit %>% filter(value > 0),
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE, name ="") +
labs(title = "Land Cover, 2011") +
mapTheme +
theme(legend.direction="horizontal")

The table below shows the approach taken to recoded existing land
cover codes into the categories used in our analysis. In the code block
below new rasters are generated and names are applied.
Naming ensures that when the raster is integrated with the fishnet, the
column reflects the appropriate raster.
| Open Space as well as Low, Medium and High Intensity
Development |
Developed |
| Deciduous, Evergreen, and Mixed Forest |
Forest |
| Pasture/Hay and Cultivated Crops |
Farm |
| Woody and Emergent Herbaceous Wetlands |
Woodlands |
| Barren Land, Dwarf Scrub, and Grassland/Herbaceous |
Other Undeveloped |
| Water |
Water |
developed <- lc_2011 == 21 | lc_2011 == 22 | lc_2011 == 23 | lc_2011 == 24
forest <- lc_2011 == 41 | lc_2011 == 42 | lc_2011 == 43
farm <- lc_2011 == 81 | lc_2011 == 82
wetlands <- lc_2011 == 90 | lc_2011 == 95
otherUndeveloped <- lc_2011 == 52 | lc_2011 == 71 | lc_2011 == 31
water <- lc_2011 == 11
names(developed) <- "developed"
names(forest) <- "forest"
names(farm) <- "farm"
names(wetlands) <- "wetlands"
names(otherUndeveloped) <- "otherUndeveloped"
names(water) <- "water"
aggregateRaster <- function(inputRasterList, theFishnet) {
#create an empty fishnet with the same dimensions as the input fishnet
theseFishnets <- theFishnet %>% dplyr::select()
#for each raster in the raster list
for (i in inputRasterList) {
#create a variable name corresponding to the ith raster
varName <- names(i)
#convert raster to points as an sf
thesePoints <-
rasterToPoints(i) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(theFishnet)) %>%
filter(.[[1]] == 1)
#aggregate to the fishnet
thisFishnet <-
aggregate(thesePoints, theFishnet, length) %>%
mutate(!!varName := ifelse(is.na(.[[1]]),0,1))
#add to the larger fishnet
theseFishnets <- cbind(theseFishnets,thisFishnet)
}
#output all aggregates as one large fishnet
return(theseFishnets)
}
theRasterList <- c(developed,forest,farm,wetlands,otherUndeveloped,water)
aggregatedRasters <-
aggregateRaster(theRasterList, dvMSA_fishnet) %>%
dplyr::select(developed,forest,farm,wetlands,otherUndeveloped,water) %>%
mutate_if(is.numeric,as.factor)
aggregatedRasters %>%
gather(var,value,developed:water) %>%
st_cast("POLYGON") %>% #just to make sure no weird geometries slipped in
mutate(X = xyC(.)$x,
Y = xyC(.)$y) %>%
ggplot() +
geom_sf(data=dvMSA) +
geom_point(aes(X,Y, colour=as.factor(value))) +
facet_wrap(~var) +
scale_colour_manual(values = palette2,
labels=c("Other","Land Cover"),
name = "") +
labs(title = "Land Cover Types, 2011",
subtitle = "As fishnet centroids") +
mapTheme

3: Pulling Demographic Data
# setting up api key
census_api_key("b83a23afee4a8ed0fa131e449869e6577b87151e", overwrite = TRUE, install = TRUE)
Delaware Valley MSA, Pennsylvania Counties:
- Bucks County
- Chester County
- Delaware County
- Montgomery County
- Philadelphia County
# pulling data from census for 2011
dvpop_2011 <- get_acs(geography = "tract",
variables = c("B01003_001E"),
year = 2011,
state = "PA",
county = c("Bucks", "Chester", "Delaware", "Montgomery", "Philadelphia"),
geometry = TRUE,
output = "wide") %>%
rename(pop_2011 = B01003_001E) %>%
dplyr::select(GEOID, NAME, pop_2011, geometry)%>%
st_transform(st_crs(dvMSA_fishnet))
# pulling data from census for 2021
dvpop_2021 <- get_acs(geography = "tract",
variables = c("B01003_001E"),
year = 2021,
state = "PA",
county = c("Bucks", "Chester", "Delaware", "Montgomery", "Philadelphia"),
geometry = TRUE,
output = "wide") %>%
rename(pop_2021 = B01003_001E) %>%
dplyr::select(GEOID, NAME, pop_2021, geometry)%>%
st_transform(st_crs(dvMSA_fishnet))
## grid arrange tract 2011 v 2021
# WHY are values NA????
grid.arrange(
ggplot() +
geom_sf(data = dvpop_2011, aes(fill=factor(ntile(pop_2011,5))), colour=NA) +
scale_fill_manual(values = palette5,
labels=quintileBreaks(dvpop_2011,"pop_2011"),
name="Quintile\nBreaks") +
labs(title="Population, Delware Valley PA counties by tract: 2011") +
mapTheme,
ggplot() +
geom_sf(data = dvpop_2021, aes(fill=factor(ntile(pop_2021,5))), colour=NA) +
scale_fill_manual(values = palette5,
labels=quintileBreaks(dvpop_2021,"pop_2021"),
name="Quintile\nBreaks") +
labs(title="Population, Delware Valley PA counties by tract: 2021") +
mapTheme, ncol=2)

3.1: Interpolating Population and Fishnet
dvMSA_fishnet <-
dvMSA_fishnet %>%
rownames_to_column("fishnetID") %>%
mutate(fishnetID = as.numeric(fishnetID)) %>%
dplyr::select(fishnetID)
fishnetPopulation11 <-
st_interpolate_aw(dvpop_2011["pop_2011"], dvMSA_fishnet, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(dvMSA_fishnet %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(pop_2011 = replace_na(pop_2011,0)) %>%
dplyr::select(pop_2011)
fishnetPopulation21 <-
st_interpolate_aw(dvpop_2021["pop_2021"],dvMSA_fishnet, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(dvMSA_fishnet %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(pop_2021 = replace_na(pop_2021,0)) %>%
dplyr::select(pop_2021)
fishnetPopulation <-
cbind(fishnetPopulation11,fishnetPopulation21) %>%
dplyr::select(pop_2011,pop_2021) %>%
mutate(pop_Change = pop_2021 - pop_2011)
grid.arrange(
ggplot() +
geom_sf(data=dvpop_2021, aes(fill=factor(ntile(pop_2021,5))),colour=NA) +
scale_fill_manual(values = palette5,
labels=substr(quintileBreaks(dvpop_2021,"pop_2021"),1,4),
name="Quintile\nBreaks") +
labs(title="Population, Delaware Valley MSA, PA: 2021",
subtitle="Represented as tracts; Boundaries omitted") +
mapTheme,
ggplot() +
geom_sf(data=fishnetPopulation, aes(fill=factor(ntile(pop_2021,5))),colour=NA) +
scale_fill_manual(values = palette5,
labels=substr(quintileBreaks(fishnetPopulation,"pop_2021"),1,4),
name="Quintile\nBreaks") +
labs(title="Population, Delaware Valley MSA, PA: 2021",
subtitle="Represented as fishnet gridcells; Boundaries omitted") +
mapTheme, ncol=2)

4: Highway Distance
#dvHighways <-
# st_read("C:/Users/ferna/OneDrive/Documents/ArcGIS/Projects/CPLN 6750/HW5/dv_roads.geojson") %>%
# st_transform(st_crs(dvMSA)) %>%
# st_intersection(dvMSA)
dvHighways <-
st_read("/Users/luyiiwong/Documents/GitHub/LandUseModeling_HW5/DelawareValley/dv_roads.geojson") %>%
st_transform(st_crs(dvMSA)) %>%
st_intersection(dvMSA)
ggplot() +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=layer),size=1.5) +
geom_sf(data=dvHighways) +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "New Development and Highways",
subtitle = "As fishnet centroids") +
mapTheme

emptyRaster <- lc_change
emptyRaster[] <- NA
dvHighways_spdf <- as(dvHighways, "Spatial")
highway_raster <- rasterize(dvHighways, emptyRaster)
#highway_raster <-
#as(dvHighways,'Spatial') %>%
#rasterize(.,emptyRaster)
highway_raster_distance <- distance(highway_raster)
names(highway_raster_distance) <- "distance_highways"
highwayPoints <-
rasterToPoints(highway_raster_distance) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(dvMSA_fishnet))
highwayPoints_fishnet <-
aggregate(highwayPoints, dvMSA_fishnet, mean) %>%
mutate(distance_highways = ifelse(is.na(distance_highways),0,distance_highways))
ggplot() +
geom_sf(data=dvMSA) +
geom_point(data=highwayPoints_fishnet, aes(x=xyC(highwayPoints_fishnet)[,1],
y=xyC(highwayPoints_fishnet)[,2],
colour=factor(ntile(distance_highways,5))),size=1.5) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(highwayPoints_fishnet,"distance_highways"),1,8),
name="Quintile/nBreaks") +
geom_sf(data=dvHighways, colour = "red") +
labs(title = "Distance to Highways",
subtitle = "As fishnet centroids; Highways visualized in red") +
mapTheme

5: Spatial lag
nn_function <- function(measureFrom,measureTo,k) {
#convert the sf layers to matrices
measureFrom_Matrix <-
as.matrix(measureFrom)
measureTo_Matrix <-
as.matrix(measureTo)
nn <-
get.knnx(measureTo, measureFrom, k)$nn.dist
output <-
as.data.frame(nn) %>%
rownames_to_column(var = "thisPoint") %>%
gather(points, point_distance, V1:ncol(.)) %>%
arrange(as.numeric(thisPoint)) %>%
group_by(thisPoint) %>%
summarize(pointDistance = mean(point_distance)) %>%
arrange(as.numeric(thisPoint)) %>%
dplyr::select(-thisPoint) %>%
pull()
return(output)
}
fishnet$lagDevelopment <-
nn_function(xyC(fishnet),
xyC(filter(aggregatedRasters,developed==1)),
2)
ggplot() +
geom_sf(data=dvMSA) +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],
colour= log(lagDevelopment), size=.001)) +
labs(title = "Spatial Lag to 2011 Development",
subtitle = "As fishnet centroids")

#how should we interpret this?
hist(fishnet$lagDevelopment)

# make histogram
# try viridis
# log color/lagdevelopment
# filter out og developed cells
6: Create MSA Counties
options(tigris_class = "sf")
studyAreaCounties <-
counties("Pennsylvania") %>%
st_transform(st_crs(dvMSA)) %>%
dplyr::select(NAME) %>%
.[st_buffer(dvMSA,-500), , op=st_intersects]
ggplot() +
geom_sf(data=studyAreaCounties) +
labs(title = "Study Area Counties") +
mapTheme

7: Create the Final Dataset
Once we join the data set with out county boundaries, all the
lc_change =1 is dropped
# can we go through this code - specifically the mutate function
# making sure logic is right, that change is from non-developed to developed
# basically ignoring classifications that are wrong
dat <-
cbind(fishnet, highwayPoints_fishnet, fishnetPopulation, aggregatedRasters)%>%
dplyr::select(layer, developed, forest, farm, wetlands, otherUndeveloped, water,
pop_2011, pop_2021, pop_Change, distance_highways,lagDevelopment) %>%
st_join(studyAreaCounties) %>%
mutate(developed10 = ifelse(layer == 1 & developed == 1, 0, developed)) %>%
filter(water == 0)
8: Exploratroy Analysis
It seems like land change only occured from undeveloped to water.
Therefore, there is no new development…
dat %>%
dplyr::select(distance_highways,lagDevelopment,layer) %>%
gather(Variable, Value, -layer, -geometry) %>%
ggplot(., aes(layer, Value, fill=layer)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of the Continuous Variables") +
plotTheme

dat %>%
dplyr::select(pop_2011,pop_2021,pop_Change,layer) %>%
gather(Variable, Value, -layer, -geometry) %>%
ggplot(., aes(layer, Value, fill=layer)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of Factor Variables") +
plotTheme

dat %>%
dplyr::select(layer:otherUndeveloped,developed) %>%
gather(Land_Cover_Type, Value, -layer, -geometry) %>%
st_set_geometry(NULL) %>%
group_by(layer, Land_Cover_Type) %>%
summarize(n = sum(as.numeric(Value))) %>%
ungroup() %>%
mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
filter(layer == 1) %>%
dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
kable() %>% kable_styling(full_width = F)
|
Land_Cover_Type
|
Conversion_Rate
|
|
developed
|
0.01%
|
|
farm
|
0.43%
|
|
forest
|
0.25%
|
|
otherUndeveloped
|
0.05%
|
|
wetlands
|
0.01%
|
9: Predicting for 2021 Model
set.seed(3456)
trainIndex <-
createDataPartition(dat$developed, p = .50,
list = FALSE,
times = 1)
datTrain <- dat[ trainIndex,]
datTest <- dat[-trainIndex,]
Model1 <- glm(layer ~ wetlands + forest + farm + otherUndeveloped,
family="binomial"(link="logit"), data = datTrain)
Model2 <- glm(layer ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment,
family="binomial"(link="logit"), data = datTrain)
Model3 <- glm(layer ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_2011,
family="binomial"(link="logit"), data = datTrain)
Model4 <- glm(layer ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_2011 +
pop_2021,
family="binomial"(link="logit"), data = datTrain)
Model5 <- glm(layer ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change,
family="binomial"(link="logit"), data = datTrain)
Model6 <- glm(layer ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change +
distance_highways,
family="binomial"(link="logit"), data = datTrain)
modelList <- paste0("Model", 1:6)
map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
setNames(paste0("Model",1:6)) %>%
gather(Model,McFadden) %>%
ggplot(aes(Model,McFadden)) +
geom_bar(stat="identity") +
labs(title= "McFadden R-Squared by Model") +
plotTheme

testSetProbs <-
data.frame(class = datTest$layer,
probs = predict(Model6, datTest, type="response"))
ggplot(testSetProbs, aes(probs)) +
geom_density(aes(fill=class), alpha=0.5) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "Histogram of test set predicted probabilities",
x="Predicted Probabilities",y="Density") +
plotTheme

9.1: Accuracy
options(yardstick.event_first = FALSE)
testSetProbs <-
testSetProbs %>%
mutate(predClass_05 = as.factor(ifelse(testSetProbs$probs >= 0.05 ,1,0)),
predClass_17 = as.factor(ifelse(testSetProbs$probs >= 0.17 ,1,0)))
testSetProbs %>%
dplyr::select(-probs) %>%
gather(Variable, Value, -class) %>%
group_by(Variable) %>%
summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>%
kable() %>%
kable_styling(full_width = F)
|
Variable
|
Sensitivity
|
Specificity
|
Accuracy
|
|
predClass_05
|
0.99
|
0.03
|
0.99
|
|
predClass_17
|
1.00
|
0.00
|
0.99
|
predsForMap <-
dat %>%
mutate(probs = predict(Model6, dat, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_17_Pct = as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
dplyr::select(layer,Threshold_5_Pct,Threshold_17_Pct) %>%
gather(Variable,Value, -geometry) %>%
st_cast("POLYGON")
ggplot() +
geom_point(data=predsForMap, aes(x=xyC(predsForMap)[,1], y=xyC(predsForMap)[,2], colour=Value)) +
facet_wrap(~Variable) +
scale_colour_manual(values = palette2, labels=c("No Change","New Development"),
name="") +
labs(title="Development Predictions - Low Threshold") +
mapTheme

ConfusionMatrix.metrics <-
dat %>%
mutate(probs = predict(Model6, dat, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_17_Pct = as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
mutate(TrueP_05 = ifelse(layer == 1 & Threshold_5_Pct == 1, 1,0),
TrueN_05 = ifelse(layer == 0 & Threshold_5_Pct == 0, 1,0),
TrueP_17 = ifelse(layer == 1 & Threshold_17_Pct == 1, 1,0),
TrueN_17 = ifelse(layer == 0 & Threshold_17_Pct == 0, 1,0)) %>%
dplyr::select(., starts_with("True")) %>%
gather(Variable, Value, -geometry) %>%
st_cast("POLYGON")
ggplot(data=ConfusionMatrix.metrics) +
geom_point(aes(x=xyC(ConfusionMatrix.metrics)[,1],
y=xyC(ConfusionMatrix.metrics)[,2], colour = as.factor(Value))) +
facet_wrap(~Variable) +
scale_colour_manual(values = palette2, labels=c("Correct","Incorrect"),
name="") +
labs(title="Development Predictions - Low Threshold") + mapTheme

10: Predicting Land Cover Demand for 2031
dat <-
dat %>%
mutate(lagDevelopment = nn_function(xyC(.), xyC(filter(.,developed10 == 1)),2))
#CHANGE THIS CODE
countyPopulation_2031 <-
data.frame(
NAME =
c("Philadelphia","Chester","Montgomery","Bucks","Delaware"),
county_projection_2031 =
c(1643971, 599932, 884387, 669299, 577248)) %>%
#CHANGE LINE ABOVE - from a study DVRPC population projection
left_join(
dat %>%
st_set_geometry(NULL) %>%
group_by(NAME) %>%
summarize(county_population_2021 = round(sum(pop_2021))))
countyPopulation_2031 %>%
gather(Variable,Value, -NAME) %>%
ggplot(aes(reorder(NAME,-Value),Value)) +
geom_bar(aes(fill=Variable), stat = "identity", position = "dodge") +
scale_fill_manual(values = palette2,
labels=c("2021","2031"),
name="Population") +
labs(title="Population Change by County: 2021 - 2031",
x="County", y="Population") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
plotTheme

10.1: Predicting Development Demand
# section edited
dat_infill <-
dat %>%
#calculate population change
left_join(countyPopulation_2031) %>%
mutate(proportion_of_county_pop = pop_2021 / county_population_2021,
pop_2031.infill = proportion_of_county_pop * county_projection_2031,
pop_Change = round(pop_2031.infill - pop_2021),2) %>%
dplyr::select(-county_projection_2031, -county_population_2021,
-proportion_of_county_pop, -pop_2031.infill) %>%
#predict for 2031
mutate(predict_2031.infill = predict(Model6,. , type="response"))
dat_infill %>%
ggplot() +
geom_point(aes(x=xyC(dat_infill)[,1], y=xyC(dat_infill)[,2], colour = factor(ntile(predict_2031.infill,5)))) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(dat_infill,"predict_2031.infill"),1,4),
name="Quintile\nBreaks") +
geom_sf(data=studyAreaCounties, fill=NA, colour="black", size=1) +
labs(title= "Development Demand in 2031: Predicted Probabilities") +
mapTheme

11: Comparing Predicted Development Demand & Environmental
Sensitivity
11.2: 2021 Land Cover Data
developed21 <- lc_2021 == 21 | lc_2021 == 22 | lc_2021 == 23 | lc_2021 == 24
forest21 <- lc_2021 == 41 | lc_2021 == 42 | lc_2021 == 43
farm21 <- lc_2021 == 81 | lc_2021 == 82
wetlands21 <- lc_2021 == 90 | lc_2021 == 95
otherUndeveloped21 <- lc_2021 == 52 | lc_2021 == 71 | lc_2021 == 31
water21 <- lc_2021 == 11
names(developed21) <- "developed21"
names(forest21) <- "forest21"
names(farm21) <- "farm21"
names(wetlands21) <- "wetlands21"
names(otherUndeveloped21) <- "otherUndeveloped21"
names(water21) <- "water21"
ggplot() +
geom_sf(data=dvMSA) +
geom_raster(data = rbind(rast(lc_2011) %>% mutate(label = "2011"),
rast(lc_2021) %>% mutate(label = "2021")) %>%
na.omit %>% filter(value > 0),
aes(x,y,fill=as.factor(value))) +
facet_wrap(~label) +
scale_fill_viridis(discrete=TRUE, name ="") +
labs(title = "Land Cover, 2011 & 2021") +
mapTheme + theme(legend.position = "none")

theRasterList21 <- c(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21)
dat2 <-
aggregateRaster(theRasterList21, dat) %>%
dplyr::select(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat) %>%
st_sf() %>%
st_cast("POLYGON")
dat2 %>%
gather(var,value,developed21:water21) %>%
st_centroid() %>%
mutate(X = st_coordinates(.)[,1],
Y = st_coordinates(.)[,2]) %>%
ggplot() +
geom_sf(data=dvMSA) +
geom_point(aes(X,Y, colour=as.factor(value))) +
facet_wrap(~var) +
scale_colour_manual(values = palette2,
labels=c("Other","Land Cover"),
name = "") +
labs(title = "Land Cover Types, 2021",
subtitle = "As fishnet centroids") +
mapTheme

11.3: Sensitive Land Cover Lost
dat2 <-
dat2 %>%
mutate(sensitive_lost21 = ifelse(forest == 1 & forest21 == 0 |
wetlands == 1 & wetlands21 == 0,1,0))
ggplot() +
geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitive_lost21))) +
scale_colour_manual(values = palette2,
labels=c("No Change","Sensitive Lost"),
name = "") +
labs(title = "Sensitive lands lost: 2011 - 2021",
subtitle = "As fishnet centroids") +
mapTheme

11.4: Landscape Fragmentation
sensitiveRegions <-
raster::clump(wetlands21 + forest21) %>%
rasterToPolygons() %>%
st_as_sf() %>%
group_by(clumps) %>%
summarize() %>%
mutate(Acres = as.numeric(st_area(.) * 0.0000229568)) %>%
filter(Acres > 3954) %>%
dplyr::select() %>%
raster::rasterize(.,emptyRaster)
sensitiveRegions[sensitiveRegions > 0] <- 1
names(sensitiveRegions) <- "sensitiveRegions"
dat2 <-
aggregateRaster(c(sensitiveRegions), dat2) %>%
dplyr::select(sensitiveRegions) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat2) %>%
st_sf()
ggplot() +
geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitiveRegions))) +
scale_colour_manual(values = palette2,
labels=c("Other","Sensitive Regions"),
name="") +
labs(title = "Sensitive regions",
subtitle = "Continous areas of either wetlands or forests\ngreater than 1 acre") +
mapTheme

11.5: Summarize by County
county_specific_metrics <-
dat2 %>%
#predict development demand from our model
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
#get a count count of grid cells by county which we can use to calculate rates below
left_join(st_set_geometry(dat, NULL) %>% group_by(NAME) %>% summarize(count = n())) %>%
#calculate summary statistics by county
group_by(NAME) %>%
summarize(Total_Farmland = sum(farm21) / max(count),
Total_Forest = sum(forest21) / max(count),
Total_Wetlands = sum(wetlands21) / max(count),
Total_Undeveloped = sum(otherUndeveloped21) / max(count),
Sensitive_Land_Lost = sum(sensitive_lost21) / max(count),
Sensitive_Regions = sum(sensitiveRegions) / max(count),
Mean_Development_Demand = mean(Development_Demand)) %>%
#get population data by county
left_join(countyPopulation_2031 %>%
mutate(Population_Change = county_projection_2031 - county_population_2021,
Population_Change_Rate = Population_Change / county_projection_2031) %>%
dplyr::select(NAME,Population_Change_Rate)) %>%
na.omit()
county_specific_metrics %>%
gather(Variable, Value, -NAME, -geometry) %>%
mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
"Total_Farmland","Total_Undeveloped","Total_Forest",
"Total_Wetlands","Sensitive_Land_Lost","Sensitive_Regions",
ordered = TRUE))) %>%
mutate(Planning_Designation = case_when(
Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
Variable == "Total_Farmland" | Variable == "Total_Undeveloped" ~ "Suitable",
TRUE ~ "Not Suitable")) %>%
ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
geom_bar(stat="identity", position=position_dodge(), colour="black") +
facet_wrap(~NAME, ncol=5) +
coord_flip() +
scale_y_continuous(breaks = seq(.25, 1, by = .25)) +
geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
scale_fill_manual(values=c("black","red","darkgreen")) +
labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

12: Scenario 1: Allocation
chester <-
dat2 %>%
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
filter(NAME == "Chester")
chester_landUse <- rbind(
filter(chester, forest21 == 1 | wetlands21 == 1 ) %>%
dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
filter(chester, developed21 == 1) %>%
dplyr::select() %>% mutate(Land_Use = "Developed"))
grid.arrange(
ggplot() +
geom_sf(data=chester, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
geom_point(data=chester_landUse, aes(x=xyC(chester_landUse)[,1],
y=xyC(chester_landUse)[,2], colour=Land_Use),
shape = 15, size = 2) +
geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
scale_fill_manual(values = palette5, name="Development\nDemand",
labels=substr(quintileBreaks(chester,"Development_Demand"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Development Potential, 2031: Chester") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),
ggplot() +
geom_sf(data=chester, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
geom_point(data=chester_landUse, aes(x=xyC(chester_landUse)[,1],
y=xyC(chester_landUse)[,2], colour=Land_Use),
shape = 15, size = 2) +
geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
scale_fill_manual(values = palette5, name="Population\nChange",
labels=substr(quintileBreaks(chester,"pop_Change"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Projected Population, 2031: Chester") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

13: Scenario 2 Transportation
13.1: Highway Distance
#dvHighways <-
# st_read("C:/Users/ferna/OneDrive/Documents/ArcGIS/Projects/CPLN 6750/HW5/dv_roads.geojson") %>%
# st_transform(st_crs(dvMSA)) %>%
# st_intersection(dvMSA)
dvTransit <-
st_read("/Users/luyiiwong/Documents/GitHub/LandUseModeling_HW5/DelawareValley/dv_transit.geojson") %>%
st_transform(st_crs(dvMSA)) %>%
st_intersection(dvMSA)
ggplot() +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=layer),size=1.5) +
geom_sf(data=dvTransit) +
scale_colour_manual(values = palette2,
labels=c("No Change","N`w Development")) +
labs(title = "New Development and New Transit",
subtitle = "As fishnet centroids") +
mapTheme

emptyRaster <- lc_change
emptyRaster[] <- NA
dvTransit_spdf <- as(dvTransit, "Spatial")
transit_raster <- rasterize(dvTransit, emptyRaster)
#highway_raster <-
#as(dvTransit,'Spatial') %>%
#rasterize(.,emptyRaster)
transit_raster_distance <- distance(transit_raster)
names(transit_raster_distance) <- "distance_transit"
transitPoints <-
rasterToPoints(transit_raster_distance) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(dvMSA_fishnet))
transitPoints_fishnet <-
aggregate(transitPoints, dvMSA_fishnet, mean) %>%
mutate(distance_transit = ifelse(is.na(distance_transit),0,distance_transit))
ggplot() +
geom_sf(data=dvMSA) +
geom_point(data=transitPoints_fishnet, aes(x=xyC(transitPoints_fishnet)[,1],
y=xyC(transitPoints_fishnet)[,2],
colour=factor(ntile(distance_transit,5))),size=1.5) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(transitPoints_fishnet,"distance_transit"),1,8),
name="Quintile/nBreaks") +
geom_sf(data=dvTransit, colour = "red") +
labs(title = "Distance to transit",
subtitle = "As fishnet centroids; transit visualized in red") +
mapTheme

13.2: Create a new dataset for the model
changing the distance from highway to distance to transit
dat.2 <-
cbind(fishnet, transitPoints_fishnet, fishnetPopulation, aggregatedRasters)%>%
dplyr::select(layer, developed, forest, farm, wetlands, otherUndeveloped, water,
pop_2011, pop_2021, pop_Change, distance_transit,lagDevelopment) %>%
st_join(studyAreaCounties) %>%
mutate(developed10 = ifelse(layer == 1 & developed == 1, 0, developed)) %>%
filter(water == 0)
13.3: Create training set
set.seed(3456)
trainIndex <-
createDataPartition(dat$developed, p = .50,
list = FALSE,
times = 1)
transit.datTrain <- dat.2[ trainIndex,]
transit.datTest <- dat.2[-trainIndex,]
# test model with transit
transitModel <- glm(layer ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change +
distance_transit,
family="binomial"(link="logit"), data = transit.datTrain)
summary(transitModel)
##
## Call:
## glm(formula = layer ~ wetlands + forest + farm + otherUndeveloped +
## lagDevelopment + pop_Change + distance_transit, family = binomial(link = "logit"),
## data = transit.datTrain)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -9.258e+00 1.385e+00 -6.686 2.30e-11 ***
## wetlands1 5.393e+00 1.537e+00 3.509 0.000449 ***
## forest1 6.425e+00 1.377e+00 4.665 3.08e-06 ***
## farm1 6.984e+00 1.381e+00 5.058 4.24e-07 ***
## otherUndeveloped1 7.782e+00 1.400e+00 5.560 2.69e-08 ***
## lagDevelopment -2.151e-03 4.284e-04 -5.020 5.16e-07 ***
## pop_Change 9.339e-03 2.463e-03 3.791 0.000150 ***
## distance_transit -2.513e-04 9.403e-05 -2.672 0.007534 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1716.3 on 19904 degrees of freedom
## Residual deviance: 1461.8 on 19897 degrees of freedom
## AIC: 1477.8
##
## Number of Fisher Scoring iterations: 12
#variables are highly significant
#r-square value: 14.8%
testSetProbs.2 <-
data.frame(class = datTest$layer,
probs = predict(transitModel, transit.datTest, type="response"))
ggplot(testSetProbs.2, aes(probs)) +
geom_density(aes(fill=class), alpha=0.5) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "Histogram of test set predicted probabilities",
x="Predicted Probabilities",y="Density") +
plotTheme

13.4: Creating new dataframe for pop projection
dat.new <-
aggregateRaster(theRasterList21, dat.2) %>%
dplyr::select(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat.2) %>%
st_sf() %>%
st_cast("POLYGON")
# considering land fragmentation
dat.new <-
aggregateRaster(c(sensitiveRegions), dat.new) %>%
dplyr::select(sensitiveRegions) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat.2) %>%
st_sf()
13.5: Land allocation - population projection and development
forcast
chester.new <-
dat.new %>%
mutate(Development_Demand = predict(transitModel, dat.new, type="response")) %>%
filter(NAME == "Chester")
chester_landUse.new <- rbind(
filter(chester, forest21 == 1 | wetlands21 == 1 ) %>%
dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
filter(chester, developed21 == 1) %>%
dplyr::select() %>% mutate(Land_Use = "Developed"))
grid.arrange(
ggplot() +
geom_sf(data=chester.new, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
geom_point(data=chester_landUse.new, aes(x=xyC(chester_landUse.new)[,1],
y=xyC(chester_landUse.new)[,2], colour=Land_Use),
shape = 15, size = 2) +
geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
scale_fill_manual(values = palette5, name="Development\nDemand",
labels=substr(quintileBreaks(chester.new,"Development_Demand"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Development Potential, 2031: Chester",
subtitle = "Additional Transit") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),
ggplot() +
geom_sf(data=chester.new, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
geom_point(data=chester_landUse.new, aes(x=xyC(chester_landUse.new)[,1],
y=xyC(chester_landUse.new)[,2], colour=Land_Use),
shape = 15, size = 2) +
geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
scale_fill_manual(values = palette5, name="Population\nChange",
labels=substr(quintileBreaks(chester.new,"pop_Change"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Projected Population, 2031: Chester",
subtitle = "Additional Transit") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

LS0tCnRpdGxlOiAiRGVsYXdhcmUgVmFsbGV5IE1TQSBVcmJhbiBHcm93dGggTW9kZWwiCmF1dGhvcjogIkx1IFlpaSBXb25nICYgSmF2aWVyIEZlcm5hbmRleiIKZGF0ZTogIjIwMjQtMDUtMTAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3IgbG9hZF9wYWNrYWdlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkocmFzdGVyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkodGlkeWNlbnN1cykKbGlicmFyeSh0aWdyaXMpCmxpYnJhcnkoRk5OKQojbGlicmFyeShRdWFudFBzeWMpICMgSkUgTm90ZTogaW4gUiA0LjEsIFF1YW50UHN5YyBwYWNrYWdlIG5vdCBhdmFpbGFibGUuCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoeWFyZHN0aWNrKQpsaWJyYXJ5KHBzY2wpCmxpYnJhcnkocGxvdFJPQykgCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShwUk9DKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoaWdyYXBoKQoKcGxvdFRoZW1lIDwtIHRoZW1lKAogIHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBibGFuawogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogIHBsb3QuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgI3BhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3VyPSIjRjBGMEYwIiksCiAgIyBGb3JtYXQgdGhlIGdyaWQKICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXI9IiNEMEQwRDAiLHNpemU9Ljc1KSwKICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSkKCm1hcFRoZW1lIDwtIHRoZW1lKHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgICAgICAgICAgICBheGlzLmxpbmU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ib3JkZXI9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXIgPSAndHJhbnNwYXJlbnQnKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCAKICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSwgJ2NtJyksCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgxLCAiY20iKSwgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMC4yLCAiY20iKSkKCnBhbGV0dGUyIDwtIGMoIiM0MWI2YzQiLCIjMjUzNDk0IikKcGFsZXR0ZTQgPC0gYygiI2ExZGFiNCIsIiM0MWI2YzQiLCIjMmM3ZmI4IiwiIzI1MzQ5NCIpCnBhbGV0dGU1IDwtIGMoIiNmZmZmY2MiLCIjYTFkYWI0IiwiIzQxYjZjNCIsIiMyYzdmYjgiLCIjMjUzNDk0IikKcGFsZXR0ZTEwIDwtIGMoIiNmN2ZjZjAiLCIjZTBmM2RiIiwiI2NjZWJjNSIsIiNhOGRkYjUiLCIjN2JjY2M0IiwKICAgICAgICAgICAgICAgIiM0ZWIzZDMiLCIjMmI4Y2JlIiwiIzA4NjhhYyIsIiMwODQwODEiLCIjZjdmY2YwIikKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiN0aGlzIGZ1bmN0aW9uIGNvbnZlcnRzIGEgY29sdW1uIGluIHRvIHF1aW50aWxlcy4gSXQgaXMgdXNlZCBmb3IgbWFwcGluZy4KcXVpbnRpbGVCcmVha3MgPC0gZnVuY3Rpb24oZGYsdmFyaWFibGUpIHsKICAgIGFzLmNoYXJhY3RlcihxdWFudGlsZShkZltbdmFyaWFibGVdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjKC4wMSwuMiwuNCwuNiwuOCksbmEucm09VCkpCn0KCiNUaGlzIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGNvbnZlcnQgYSBwb2x5Z29uIHNmIHRvIGNlbnRyb2lkcyB4eSBjb29yZHMuCnh5QyA8LSBmdW5jdGlvbihhUG9seWdvblNGKSB7CiAgYXMuZGF0YS5mcmFtZSgKICAgIGNiaW5kKHg9c3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoYVBvbHlnb25TRikpWywxXSwKICAgICAgICAgIHk9c3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoYVBvbHlnb25TRikpWywyXSkpCn0gCgojdGhpcyBmdW5jdGlvbiBjb252ZXJ0IGEgcmFzdGVyIHRvIGEgZGF0YSBmcmFtZSBzbyBpdCBjYW4gYmUgcGxvdHRlZCBpbiBnZ3Bsb3QKcmFzdCA8LSBmdW5jdGlvbihpblJhc3RlcikgewogIGRhdGEuZnJhbWUoCiAgICB4eUZyb21DZWxsKGluUmFzdGVyLCAxOm5jZWxsKGluUmFzdGVyKSksIAogICAgdmFsdWUgPSBnZXRWYWx1ZXMoaW5SYXN0ZXIpKSB9CmBgYAoKIyAxOiBTZXQgdXAKCmBgYHtyIGxvYWRfZGF0YSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9CmR2TVNBIDwtIAogIHN0X3JlYWQoIi9Vc2Vycy9sdXlpaXdvbmcvRG9jdW1lbnRzL0xhbmQgVXNlICYgRW52aXJvbm1lbnRhbCBNb2RlbGluZy9Bc3NpZ25tZW50NS9kdl9jb3VudGllcy9kdl9jb3VudGllcy5zaHAiKSAKCmxjXzIwMTEgPSByYXN0ZXIoIi9Vc2Vycy9sdXlpaXdvbmcvRG9jdW1lbnRzL0xhbmQgVXNlICYgRW52aXJvbm1lbnRhbCBNb2RlbGluZy9Bc3NpZ25tZW50NS9sY18yMDExX2NsaXBfUmVzYW1wbGUudGlmIikKbGNfMjAyMSA9IHJhc3RlcigiL1VzZXJzL2x1eWlpd29uZy9Eb2N1bWVudHMvTGFuZCBVc2UgJiBFbnZpcm9ubWVudGFsIE1vZGVsaW5nL0Fzc2lnbm1lbnQ1L2xjXzIwMjFfY2xpcF9SZXNhbXBsZS50aWYiKQoKbGNfY2hhbmdlIDwtIGxjXzIwMTErbGNfMjAyMQoKYGBgCgojIDI6IENhbGN1bGF0aW5nIExhbmQgQ292ZXIgQ2hhbmdlCmBgYHtyfQojY3JlYXRpbmcgYSBtYXRyaXggdG8gY2xhc3NpZnkgdXJiYW4gYW5kIG5vbi11cmJhbiBsYW5kIHVzZQpyZWNsYXNzTWF0cml4IDwtIAogIG1hdHJpeChjKAogICAgMCwxMiwwLAogICAgMTIsMjQsMSwKICAgIDI0LEluZiwwKSwKICBuY29sPTMsIGJ5cm93PVQpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIHJlY2xhc3NpZnlpbmcgbGFuZCBjb3ZlciBmb3IgYm90aCB5ZWFycwpkZXZlbG9wZWRfMjAxMSA8LSAKICByZWNsYXNzaWZ5KGxjXzIwMTEscmVjbGFzc01hdHJpeCkKCmRldmVsb3BlZF8yMDIxIDwtIAogIHJlY2xhc3NpZnkobGNfMjAyMSxyZWNsYXNzTWF0cml4KQoKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgY2FsY3VsYXRpbmcgbGFuZCB1c2UgY2hhbmdlIGZyb20gMjAxMSB0byAyMDIxCmRldmVsb3BtZW50X2NoYW5nZSA8LSBkZXZlbG9wZWRfMjAxMStkZXZlbG9wZWRfMjAyMQoKIyB0aGUgdmFsdWVzIHRoYXQgYXJlID0xIGluZGljYXRlIHRoYXQgdGhlcmUgd2FzIENIQU5HRSBiZXR3ZWVuIDIwMTEgYW5kIDIwMjEKIyBoaXN0b2dyYW0gc2hvd3MgdGhhdCB0aGVyZSB3ZXJlIHZlcnkgZmV3IHZhbHVlcyBpbiB0aGUgc3R1ZHkgYXJlYQpoaXN0KGRldmVsb3BtZW50X2NoYW5nZSkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgcmVjbGFzc2lmeWluZyB2YWx1ZXMgdGhhdCBhcmUgbm90IGVxdWFsIHRvIDEgdG8gTkEgaW4gZGV2ZWxvcG1lbnQgY2hhbmdlCiMgdGhpcyBtZWFucyB0aGF0IHZhbHVlcyAwIGFuZCAyIGJlY29tZSBOQQpkZXZlbG9wbWVudF9jaGFuZ2VbZGV2ZWxvcG1lbnRfY2hhbmdlICE9IDFdIDwtIE5BCgojIHBsb3R0aW5nIHRoZSB2YWx1ZXMgb2YgZGV2ZWxvcG1lbnQgY2hhbmdlID0gMSBzcGF0aWFsbHkKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChkZXZlbG9wbWVudF9jaGFuZ2UpICU+JSBuYS5vbWl0LCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwgbmFtZSA9IkxhbmQgQ292ZXJcbkNoYW5nZSIpICsgCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgbGFuZCB1c2UgY2hhbmdlIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMi4xOiBDcmVhdGluZyB0aGUgZmlzaG5ldApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmR2TVNBX2Zpc2huZXQgPC0gCiAgc3RfbWFrZV9ncmlkKGR2TVNBLCA1MDApICU+JQogIHN0X3NmKCkKCmR2TVNBX2Zpc2huZXQgPC0KICBkdk1TQV9maXNobmV0W2R2TVNBLF0KYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRX0KZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQV9maXNobmV0KSArCiAgbGFicyh0aXRsZT0iRmlzaG5ldCwgNTAwIEZvb3QgUmVzb2x1dGlvbiIpICsKICBtYXBUaGVtZQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KI29yaWdpbmFsIHZlcnNpb24KY2hhbmdlUG9pbnRzIDwtCiAgcmFzdGVyVG9Qb2ludHMoZGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2Nycyhkdk1TQV9maXNobmV0KSkKCiMgY2hhbmdlZCB0aGlzIG5hbWluZyBmcm9tIGxjX2NoYW5nZSB0byBsYXllciB0aGVuIGNvbnZlcnRlZCBOQSB0byAwCiMgYmVjYXVzZSBpbiBmaXNobmV0IGhlcmUsIGl0J3MgY2FsbGVkIGxheWVyIG5vdCBsY19jaGFuZ2UKZmlzaG5ldCA8LSAKICBhZ2dyZWdhdGUoY2hhbmdlUG9pbnRzLCBkdk1TQV9maXNobmV0LCBzdW0pICU+JQogIG11dGF0ZShsYXllciA9IGlmZWxzZShpcy5uYShsYXllciksMCwxKSwKICAgICAgICAgbGF5ZXIgPSBhcy5mYWN0b3IobGF5ZXIpKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KSR4LCB5PXh5QyhmaXNobmV0KSR5LCBjb2xvdXI9bGF5ZXIpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBEZXZlbG9wbWVudCBDaGFuZ2UiLCBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsKICBtYXBUaGVtZQpgYGAKCiMjIDIuMjogUGxvdHRpbmcgTGFuZCBDb3ZlciBpbiAyMDExCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9ZHZNU0EpICsKICBnZW9tX3Jhc3RlcihkYXRhPXJhc3QobGNfMjAxMSkgJT4lIG5hLm9taXQgJT4lIGZpbHRlcih2YWx1ZSA+IDApLCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwgbmFtZSA9IiIpICsKICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIsIDIwMTEiKSArCiAgbWFwVGhlbWUgKwogIHRoZW1lKGxlZ2VuZC5kaXJlY3Rpb249Imhvcml6b250YWwiKQpgYGAKClRoZSB0YWJsZSBiZWxvdyBzaG93cyB0aGUgYXBwcm9hY2ggdGFrZW4gdG8gcmVjb2RlZCBleGlzdGluZyBsYW5kIGNvdmVyIGNvZGVzIGludG8gdGhlIGNhdGVnb3JpZXMgdXNlZCBpbiBvdXIgYW5hbHlzaXMuIEluIHRoZSBjb2RlIGJsb2NrIGJlbG93IG5ldyByYXN0ZXJzIGFyZSBnZW5lcmF0ZWQgYW5kIGBuYW1lc2AgYXJlIGFwcGxpZWQuIE5hbWluZyBlbnN1cmVzIHRoYXQgd2hlbiB0aGUgcmFzdGVyIGlzIGludGVncmF0ZWQgd2l0aCB0aGUgZmlzaG5ldCwgdGhlIGNvbHVtbiByZWZsZWN0cyB0aGUgYXBwcm9wcmlhdGUgcmFzdGVyLgoKfCBPbGRfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgfCBOZXdfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgT3BlbiBTcGFjZSBhcyB3ZWxsIGFzIExvdywgTWVkaXVtIGFuZCBIaWdoIEludGVuc2l0eSBEZXZlbG9wbWVudCB8IERldmVsb3BlZCB8CnwgRGVjaWR1b3VzLCBFdmVyZ3JlZW4sIGFuZCBNaXhlZCBGb3Jlc3QgfCAgRm9yZXN0IHwKfCBQYXN0dXJlL0hheSBhbmQgQ3VsdGl2YXRlZCBDcm9wcyB8IEZhcm0gfAp8IFdvb2R5IGFuZCBFbWVyZ2VudCBIZXJiYWNlb3VzIFdldGxhbmRzIHwgV29vZGxhbmRzIHwKfCBCYXJyZW4gTGFuZCwgRHdhcmYgU2NydWIsIGFuZCBHcmFzc2xhbmQvSGVyYmFjZW91cyB8IE90aGVyIFVuZGV2ZWxvcGVkIHwKfCBXYXRlciB8IFdhdGVyIHwKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGV2ZWxvcGVkIDwtIGxjXzIwMTEgPT0gMjEgfCBsY18yMDExID09IDIyIHwgbGNfMjAxMSA9PSAyMyB8IGxjXzIwMTEgPT0gMjQKZm9yZXN0IDwtIGxjXzIwMTEgPT0gNDEgfCBsY18yMDExID09IDQyIHwgbGNfMjAxMSA9PSA0MyAKZmFybSA8LSBsY18yMDExID09IDgxIHwgbGNfMjAxMSA9PSA4MiAKd2V0bGFuZHMgPC0gbGNfMjAxMSA9PSA5MCB8IGxjXzIwMTEgPT0gOTUgCm90aGVyVW5kZXZlbG9wZWQgPC0gbGNfMjAxMSA9PSA1MiB8IGxjXzIwMTEgPT0gNzEgfCBsY18yMDExID09IDMxIAp3YXRlciA8LSBsY18yMDExID09IDExCgpuYW1lcyhkZXZlbG9wZWQpIDwtICJkZXZlbG9wZWQiCm5hbWVzKGZvcmVzdCkgPC0gImZvcmVzdCIKbmFtZXMoZmFybSkgPC0gImZhcm0iCm5hbWVzKHdldGxhbmRzKSA8LSAid2V0bGFuZHMiCm5hbWVzKG90aGVyVW5kZXZlbG9wZWQpIDwtICJvdGhlclVuZGV2ZWxvcGVkIgpuYW1lcyh3YXRlcikgPC0gIndhdGVyIgpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KCmFnZ3JlZ2F0ZVJhc3RlciA8LSBmdW5jdGlvbihpbnB1dFJhc3Rlckxpc3QsIHRoZUZpc2huZXQpIHsKICAjY3JlYXRlIGFuIGVtcHR5IGZpc2huZXQgd2l0aCB0aGUgc2FtZSBkaW1lbnNpb25zIGFzIHRoZSBpbnB1dCBmaXNobmV0CiAgdGhlc2VGaXNobmV0cyA8LSB0aGVGaXNobmV0ICU+JSBkcGx5cjo6c2VsZWN0KCkKICAjZm9yIGVhY2ggcmFzdGVyIGluIHRoZSByYXN0ZXIgbGlzdAogIGZvciAoaSBpbiBpbnB1dFJhc3Rlckxpc3QpIHsKICAjY3JlYXRlIGEgdmFyaWFibGUgbmFtZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBpdGggcmFzdGVyCiAgdmFyTmFtZSA8LSBuYW1lcyhpKQogICNjb252ZXJ0IHJhc3RlciB0byBwb2ludHMgYXMgYW4gc2YKICAgIHRoZXNlUG9pbnRzIDwtCiAgICAgIHJhc3RlclRvUG9pbnRzKGkpICU+JQogICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpLCBjcnMgPSBzdF9jcnModGhlRmlzaG5ldCkpICU+JQogICAgICBmaWx0ZXIoLltbMV1dID09IDEpCiAgI2FnZ3JlZ2F0ZSB0byB0aGUgZmlzaG5ldAogICAgdGhpc0Zpc2huZXQgPC0KICAgICAgYWdncmVnYXRlKHRoZXNlUG9pbnRzLCB0aGVGaXNobmV0LCBsZW5ndGgpICU+JQogICAgICBtdXRhdGUoISF2YXJOYW1lIDo9IGlmZWxzZShpcy5uYSguW1sxXV0pLDAsMSkpCiAgI2FkZCB0byB0aGUgbGFyZ2VyIGZpc2huZXQKICAgIHRoZXNlRmlzaG5ldHMgPC0gY2JpbmQodGhlc2VGaXNobmV0cyx0aGlzRmlzaG5ldCkKICB9CiAgI291dHB1dCBhbGwgYWdncmVnYXRlcyBhcyBvbmUgbGFyZ2UgZmlzaG5ldAogICByZXR1cm4odGhlc2VGaXNobmV0cykKICB9CmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQp0aGVSYXN0ZXJMaXN0IDwtIGMoZGV2ZWxvcGVkLGZvcmVzdCxmYXJtLHdldGxhbmRzLG90aGVyVW5kZXZlbG9wZWQsd2F0ZXIpCgphZ2dyZWdhdGVkUmFzdGVycyA8LQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0LCBkdk1TQV9maXNobmV0KSAlPiUKICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZCxmb3Jlc3QsZmFybSx3ZXRsYW5kcyxvdGhlclVuZGV2ZWxvcGVkLHdhdGVyKSAlPiUKICBtdXRhdGVfaWYoaXMubnVtZXJpYyxhcy5mYWN0b3IpCgphZ2dyZWdhdGVkUmFzdGVycyAlPiUKICBnYXRoZXIodmFyLHZhbHVlLGRldmVsb3BlZDp3YXRlcikgJT4lCiAgc3RfY2FzdCgiUE9MWUdPTiIpICU+JSAgICAjanVzdCB0byBtYWtlIHN1cmUgbm8gd2VpcmQgZ2VvbWV0cmllcyBzbGlwcGVkIGluCiAgbXV0YXRlKFggPSB4eUMoLikkeCwKICAgICAgICAgWSA9IHh5QyguKSR5KSAlPiUKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGRhdGE9ZHZNU0EpICsKICAgIGdlb21fcG9pbnQoYWVzKFgsWSwgY29sb3VyPWFzLmZhY3Rvcih2YWx1ZSkpKSArCiAgICBmYWNldF93cmFwKH52YXIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIkxhbmQgQ292ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArCiAgICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgVHlwZXMsIDIwMTEiLAogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsKICAgbWFwVGhlbWUKYGBgCgojIDM6IFB1bGxpbmcgRGVtb2dyYXBoaWMgRGF0YQpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIHNldHRpbmcgdXAgYXBpIGtleSAKY2Vuc3VzX2FwaV9rZXkoImI4M2EyM2FmZWU0YThlZDBmYTEzMWU0NDk4NjllNjU3N2I4NzE1MWUiLCBvdmVyd3JpdGUgPSBUUlVFLCBpbnN0YWxsID0gVFJVRSkKYGBgCkRlbGF3YXJlIFZhbGxleSBNU0EsIFBlbm5zeWx2YW5pYSBDb3VudGllczoKCi0gQnVja3MgQ291bnR5Ci0gQ2hlc3RlciBDb3VudHkKLSBEZWxhd2FyZSBDb3VudHkKLSBNb250Z29tZXJ5IENvdW50eQotIFBoaWxhZGVscGhpYSBDb3VudHkKCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cz0naGlkZSd9CiMgcHVsbGluZyBkYXRhIGZyb20gY2Vuc3VzIGZvciAyMDExCmR2cG9wXzIwMTEgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCAKICAgICAgICAgIHZhcmlhYmxlcyA9IGMoIkIwMTAwM18wMDFFIiksIAogICAgICAgICAgeWVhciA9IDIwMTEsIAogICAgICAgICAgc3RhdGUgPSAiUEEiLCAKICAgICAgICAgIGNvdW50eSA9IGMoIkJ1Y2tzIiwgIkNoZXN0ZXIiLCAiRGVsYXdhcmUiLCAiTW9udGdvbWVyeSIsICJQaGlsYWRlbHBoaWEiKSwKICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSwgCiAgICAgICAgICBvdXRwdXQgPSAid2lkZSIpICU+JQogIHJlbmFtZShwb3BfMjAxMSA9IEIwMTAwM18wMDFFKSAlPiUKICBkcGx5cjo6c2VsZWN0KEdFT0lELCBOQU1FLCBwb3BfMjAxMSwgZ2VvbWV0cnkpJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyhkdk1TQV9maXNobmV0KSkKCgojIHB1bGxpbmcgZGF0YSBmcm9tIGNlbnN1cyBmb3IgMjAyMQpkdnBvcF8yMDIxIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDEwMDNfMDAxRSIpLCAKICAgICAgICAgIHllYXIgPSAyMDIxLCAKICAgICAgICAgIHN0YXRlID0gIlBBIiwgCiAgICAgICAgICBjb3VudHkgPSBjKCJCdWNrcyIsICJDaGVzdGVyIiwgIkRlbGF3YXJlIiwgIk1vbnRnb21lcnkiLCAiUGhpbGFkZWxwaGlhIiksCiAgICAgICAgICBnZW9tZXRyeSA9IFRSVUUsIAogICAgICAgICAgb3V0cHV0ID0gIndpZGUiKSAlPiUKICByZW5hbWUocG9wXzIwMjEgPSBCMDEwMDNfMDAxRSkgJT4lCiAgZHBseXI6OnNlbGVjdChHRU9JRCwgTkFNRSwgcG9wXzIwMjEsIGdlb21ldHJ5KSU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZHZNU0FfZmlzaG5ldCkpCgoKCgojIyBncmlkIGFycmFuZ2UgdHJhY3QgMjAxMSB2IDIwMjEKIyBXSFkgYXJlIHZhbHVlcyBOQT8/Pz8KZ3JpZC5hcnJhbmdlKApnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gZHZwb3BfMjAxMSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDExLDUpKSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1xdWludGlsZUJyZWFrcyhkdnBvcF8yMDExLCJwb3BfMjAxMSIpLAogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsKICBsYWJzKHRpdGxlPSJQb3B1bGF0aW9uLCBEZWx3YXJlIFZhbGxleSBQQSBjb3VudGllcyBieSB0cmFjdDogMjAxMSIpICsKICBtYXBUaGVtZSwKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBkdnBvcF8yMDIxLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wXzIwMjEsNSkpKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXF1aW50aWxlQnJlYWtzKGR2cG9wXzIwMjEsInBvcF8yMDIxIiksCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKwogIGxhYnModGl0bGU9IlBvcHVsYXRpb24sIERlbHdhcmUgVmFsbGV5IFBBIGNvdW50aWVzIGJ5IHRyYWN0OiAyMDIxIikgKwogIG1hcFRoZW1lLCBuY29sPTIpCgpgYGAKCiMjIDMuMTogSW50ZXJwb2xhdGluZyBQb3B1bGF0aW9uIGFuZCBGaXNobmV0CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZHZNU0FfZmlzaG5ldCA8LQogIGR2TVNBX2Zpc2huZXQgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJmaXNobmV0SUQiKSAlPiUgCiAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLm51bWVyaWMoZmlzaG5ldElEKSkgJT4lCiAgZHBseXI6OnNlbGVjdChmaXNobmV0SUQpCgpmaXNobmV0UG9wdWxhdGlvbjExIDwtCiAgc3RfaW50ZXJwb2xhdGVfYXcoZHZwb3BfMjAxMVsicG9wXzIwMTEiXSwgZHZNU0FfZmlzaG5ldCwgZXh0ZW5zaXZlPVRSVUUpICU+JQogIGFzLmRhdGEuZnJhbWUoLikgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJmaXNobmV0SUQiKSAlPiUKICBsZWZ0X2pvaW4oZHZNU0FfZmlzaG5ldCAlPiUKICAgICAgICAgICAgICBtdXRhdGUoZmlzaG5ldElEID0gYXMuY2hhcmFjdGVyKGZpc2huZXRJRCkpLAogICAgICAgICAgICAuLCBieT1jKCJmaXNobmV0SUQiPSdmaXNobmV0SUQnKSkgJT4lIAogIG11dGF0ZShwb3BfMjAxMSA9IHJlcGxhY2VfbmEocG9wXzIwMTEsMCkpICU+JQogIGRwbHlyOjpzZWxlY3QocG9wXzIwMTEpCgpmaXNobmV0UG9wdWxhdGlvbjIxIDwtCiAgc3RfaW50ZXJwb2xhdGVfYXcoZHZwb3BfMjAyMVsicG9wXzIwMjEiXSxkdk1TQV9maXNobmV0LCBleHRlbnNpdmU9VFJVRSkgJT4lCiAgYXMuZGF0YS5mcmFtZSguKSAlPiUKICByb3duYW1lc190b19jb2x1bW4odmFyID0gImZpc2huZXRJRCIpICU+JQogIGxlZnRfam9pbihkdk1TQV9maXNobmV0ICU+JQogICAgICAgICAgICAgIG11dGF0ZShmaXNobmV0SUQgPSBhcy5jaGFyYWN0ZXIoZmlzaG5ldElEKSksCiAgICAgICAgICAgIC4sIGJ5PWMoImZpc2huZXRJRCI9J2Zpc2huZXRJRCcpKSAlPiUgCiAgbXV0YXRlKHBvcF8yMDIxID0gcmVwbGFjZV9uYShwb3BfMjAyMSwwKSkgJT4lCiAgZHBseXI6OnNlbGVjdChwb3BfMjAyMSkKCmZpc2huZXRQb3B1bGF0aW9uIDwtIAogIGNiaW5kKGZpc2huZXRQb3B1bGF0aW9uMTEsZmlzaG5ldFBvcHVsYXRpb24yMSkgJT4lCiAgZHBseXI6OnNlbGVjdChwb3BfMjAxMSxwb3BfMjAyMSkgJT4lCiAgbXV0YXRlKHBvcF9DaGFuZ2UgPSBwb3BfMjAyMSAtIHBvcF8yMDExKQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aD0gMTF9CmdyaWQuYXJyYW5nZSgKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdnBvcF8yMDIxLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wXzIwMjEsNSkpKSxjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGR2cG9wXzIwMjEsInBvcF8yMDIxIiksMSw0KSwKICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArCiAgbGFicyh0aXRsZT0iUG9wdWxhdGlvbiwgRGVsYXdhcmUgVmFsbGV5IE1TQSwgUEE6IDIwMjEiLAogICAgICAgc3VidGl0bGU9IlJlcHJlc2VudGVkIGFzIHRyYWN0czsgQm91bmRhcmllcyBvbWl0dGVkIikgKwogIG1hcFRoZW1lLAoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1maXNobmV0UG9wdWxhdGlvbiwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDIxLDUpKSksY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGZpc2huZXRQb3B1bGF0aW9uLCJwb3BfMjAyMSIpLDEsNCksCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKwogIGxhYnModGl0bGU9IlBvcHVsYXRpb24sIERlbGF3YXJlIFZhbGxleSBNU0EsIFBBOiAyMDIxIiwKICAgICAgIHN1YnRpdGxlPSJSZXByZXNlbnRlZCBhcyBmaXNobmV0IGdyaWRjZWxsczsgQm91bmRhcmllcyBvbWl0dGVkIikgKwogIG1hcFRoZW1lLCBuY29sPTIpCmBgYAoKIyA0OiBIaWdod2F5IERpc3RhbmNlCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KI2R2SGlnaHdheXMgPC0KIyAgc3RfcmVhZCgiQzovVXNlcnMvZmVybmEvT25lRHJpdmUvRG9jdW1lbnRzL0FyY0dJUy9Qcm9qZWN0cy9DUExOIDY3NTAvSFc1L2R2X3JvYWRzLmdlb2pzb24iKSAlPiUKIyAgc3RfdHJhbnNmb3JtKHN0X2Nycyhkdk1TQSkpICU+JQojICBzdF9pbnRlcnNlY3Rpb24oZHZNU0EpCgpkdkhpZ2h3YXlzIDwtCiAgc3RfcmVhZCgiL1VzZXJzL2x1eWlpd29uZy9Eb2N1bWVudHMvR2l0SHViL0xhbmRVc2VNb2RlbGluZ19IVzUvRGVsYXdhcmVWYWxsZXkvZHZfcm9hZHMuZ2VvanNvbiIpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZHZNU0EpKSAlPiUKICBzdF9pbnRlcnNlY3Rpb24oZHZNU0EpCmBgYAoKYGBge3Igd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRX0KZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSxjb2xvdXI9bGF5ZXIpLHNpemU9MS41KSArCiAgZ2VvbV9zZihkYXRhPWR2SGlnaHdheXMpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgYW5kIEhpZ2h3YXlzIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQplbXB0eVJhc3RlciA8LSBsY19jaGFuZ2UKZW1wdHlSYXN0ZXJbXSA8LSBOQQoKZHZIaWdod2F5c19zcGRmIDwtIGFzKGR2SGlnaHdheXMsICJTcGF0aWFsIikKaGlnaHdheV9yYXN0ZXIgPC0gcmFzdGVyaXplKGR2SGlnaHdheXMsIGVtcHR5UmFzdGVyKQoKI2hpZ2h3YXlfcmFzdGVyIDwtIAogICNhcyhkdkhpZ2h3YXlzLCdTcGF0aWFsJykgJT4lCiAgI3Jhc3Rlcml6ZSguLGVtcHR5UmFzdGVyKQoKaGlnaHdheV9yYXN0ZXJfZGlzdGFuY2UgPC0gZGlzdGFuY2UoaGlnaHdheV9yYXN0ZXIpCm5hbWVzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSA8LSAiZGlzdGFuY2VfaGlnaHdheXMiCgpoaWdod2F5UG9pbnRzIDwtCiAgcmFzdGVyVG9Qb2ludHMoaGlnaHdheV9yYXN0ZXJfZGlzdGFuY2UpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKGR2TVNBX2Zpc2huZXQpKQoKaGlnaHdheVBvaW50c19maXNobmV0IDwtIAogIGFnZ3JlZ2F0ZShoaWdod2F5UG9pbnRzLCBkdk1TQV9maXNobmV0LCBtZWFuKSAlPiUKICBtdXRhdGUoZGlzdGFuY2VfaGlnaHdheXMgPSBpZmVsc2UoaXMubmEoZGlzdGFuY2VfaGlnaHdheXMpLDAsZGlzdGFuY2VfaGlnaHdheXMpKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1oaWdod2F5UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKGhpZ2h3YXlQb2ludHNfZmlzaG5ldClbLDJdLCAKICAgICAgICAgICAgICAgICBjb2xvdXI9ZmFjdG9yKG50aWxlKGRpc3RhbmNlX2hpZ2h3YXlzLDUpKSksc2l6ZT0xLjUpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhoaWdod2F5UG9pbnRzX2Zpc2huZXQsImRpc3RhbmNlX2hpZ2h3YXlzIiksMSw4KSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlL25CcmVha3MiKSArCiAgZ2VvbV9zZihkYXRhPWR2SGlnaHdheXMsIGNvbG91ciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byBIaWdod2F5cyIsCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkczsgSGlnaHdheXMgdmlzdWFsaXplZCBpbiByZWQiKSArCiAgbWFwVGhlbWUKYGBgCgojIDU6IFNwYXRpYWwgbGFnCmBgYCB7ciBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0V9Cm5uX2Z1bmN0aW9uIDwtIGZ1bmN0aW9uKG1lYXN1cmVGcm9tLG1lYXN1cmVUbyxrKSB7CiAgI2NvbnZlcnQgdGhlIHNmIGxheWVycyB0byBtYXRyaWNlcwogIG1lYXN1cmVGcm9tX01hdHJpeCA8LQogICAgYXMubWF0cml4KG1lYXN1cmVGcm9tKQogIG1lYXN1cmVUb19NYXRyaXggPC0KICAgIGFzLm1hdHJpeChtZWFzdXJlVG8pCiAgbm4gPC0gICAKICAgIGdldC5rbm54KG1lYXN1cmVUbywgbWVhc3VyZUZyb20sIGspJG5uLmRpc3QKICAgIG91dHB1dCA8LQogICAgYXMuZGF0YS5mcmFtZShubikgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInRoaXNQb2ludCIpICU+JQogICAgZ2F0aGVyKHBvaW50cywgcG9pbnRfZGlzdGFuY2UsIFYxOm5jb2woLikpICU+JQogICAgYXJyYW5nZShhcy5udW1lcmljKHRoaXNQb2ludCkpICU+JQogICAgZ3JvdXBfYnkodGhpc1BvaW50KSAlPiUKICAgIHN1bW1hcml6ZShwb2ludERpc3RhbmNlID0gbWVhbihwb2ludF9kaXN0YW5jZSkpICU+JQogICAgYXJyYW5nZShhcy5udW1lcmljKHRoaXNQb2ludCkpICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoLXRoaXNQb2ludCkgJT4lCiAgICBwdWxsKCkKICAKICByZXR1cm4ob3V0cHV0KSAgCn0KYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0V9CmZpc2huZXQkbGFnRGV2ZWxvcG1lbnQgPC0KICAgIG5uX2Z1bmN0aW9uKHh5QyhmaXNobmV0KSwKICAgICAgICAgICAgICAgIHh5QyhmaWx0ZXIoYWdncmVnYXRlZFJhc3RlcnMsZGV2ZWxvcGVkPT0xKSksCiAgICAgICAgICAgICAgICAyKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSwgCiAgICAgICAgICAgICAgICAgY29sb3VyPSBsb2cobGFnRGV2ZWxvcG1lbnQpLCBzaXplPS4wMDEpKSArCiAgbGFicyh0aXRsZSA9ICJTcGF0aWFsIExhZyB0byAyMDExIERldmVsb3BtZW50IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikKCiNob3cgc2hvdWxkIHdlIGludGVycHJldCB0aGlzPwpoaXN0KGZpc2huZXQkbGFnRGV2ZWxvcG1lbnQpCiMgbWFrZSBoaXN0b2dyYW0KIyB0cnkgdmlyaWRpcwojIGxvZyBjb2xvci9sYWdkZXZlbG9wbWVudAojIGZpbHRlciBvdXQgb2cgZGV2ZWxvcGVkIGNlbGxzIApgYGAKCiMgNjogQ3JlYXRlIE1TQSBDb3VudGllcyAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0Kb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQoKc3R1ZHlBcmVhQ291bnRpZXMgPC0gCiAgY291bnRpZXMoIlBlbm5zeWx2YW5pYSIpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZHZNU0EpKSAlPiUKICBkcGx5cjo6c2VsZWN0KE5BTUUpICU+JQogIC5bc3RfYnVmZmVyKGR2TVNBLC01MDApLCAsIG9wPXN0X2ludGVyc2VjdHNdCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPXN0dWR5QXJlYUNvdW50aWVzKSArCiAgbGFicyh0aXRsZSA9ICJTdHVkeSBBcmVhIENvdW50aWVzIikgKwogIG1hcFRoZW1lCmBgYAoKIyA3OiBDcmVhdGUgdGhlIEZpbmFsIERhdGFzZXQgCk9uY2Ugd2Ugam9pbiB0aGUgZGF0YSBzZXQgd2l0aCBvdXQgY291bnR5IGJvdW5kYXJpZXMsIGFsbCB0aGUgbGNfY2hhbmdlID0xIGlzIGRyb3BwZWQKYGBge3J9CiMgY2FuIHdlIGdvIHRocm91Z2ggdGhpcyBjb2RlIC0gc3BlY2lmaWNhbGx5IHRoZSBtdXRhdGUgZnVuY3Rpb24KIyBtYWtpbmcgc3VyZSBsb2dpYyBpcyByaWdodCwgdGhhdCBjaGFuZ2UgaXMgZnJvbSBub24tZGV2ZWxvcGVkIHRvIGRldmVsb3BlZAojIGJhc2ljYWxseSBpZ25vcmluZyBjbGFzc2lmaWNhdGlvbnMgdGhhdCBhcmUgd3JvbmcKZGF0IDwtIAogIGNiaW5kKGZpc2huZXQsIGhpZ2h3YXlQb2ludHNfZmlzaG5ldCwgZmlzaG5ldFBvcHVsYXRpb24sIGFnZ3JlZ2F0ZWRSYXN0ZXJzKSU+JQogIGRwbHlyOjpzZWxlY3QobGF5ZXIsIGRldmVsb3BlZCwgZm9yZXN0LCBmYXJtLCB3ZXRsYW5kcywgb3RoZXJVbmRldmVsb3BlZCwgd2F0ZXIsCiAgICAgICAgICAgICAgICBwb3BfMjAxMSwgcG9wXzIwMjEsIHBvcF9DaGFuZ2UsIGRpc3RhbmNlX2hpZ2h3YXlzLGxhZ0RldmVsb3BtZW50KSAlPiUKICBzdF9qb2luKHN0dWR5QXJlYUNvdW50aWVzKSAlPiUKICBtdXRhdGUoZGV2ZWxvcGVkMTAgPSBpZmVsc2UobGF5ZXIgPT0gMSAmIGRldmVsb3BlZCA9PSAxLCAwLCBkZXZlbG9wZWQpKSAlPiUKICBmaWx0ZXIod2F0ZXIgPT0gMCkgCmBgYAoKIyA4OiBFeHBsb3JhdHJveSBBbmFseXNpcwpJdCBzZWVtcyBsaWtlIGxhbmQgY2hhbmdlIG9ubHkgb2NjdXJlZCBmcm9tIHVuZGV2ZWxvcGVkIHRvIHdhdGVyLiBUaGVyZWZvcmUsIHRoZXJlIGlzIG5vIG5ldyBkZXZlbG9wbWVudC4uLgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCAlPiUKICBkcGx5cjo6c2VsZWN0KGRpc3RhbmNlX2hpZ2h3YXlzLGxhZ0RldmVsb3BtZW50LGxheWVyKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtbGF5ZXIsIC1nZW9tZXRyeSkgJT4lCiAgZ2dwbG90KC4sIGFlcyhsYXllciwgVmFsdWUsIGZpbGw9bGF5ZXIpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gInN1bW1hcnkiLCBmdW4ueSA9ICJtZWFuIikgKwogICAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKwogICAgbGFicyh0aXRsZT0iTmV3IERldmVsb3BtZW50IGFzIGEgRnVuY3Rpb24gb2YgdGhlIENvbnRpbnVvdXMgVmFyaWFibGVzIikgKwogICAgcGxvdFRoZW1lIApgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0ICU+JQogIGRwbHlyOjpzZWxlY3QocG9wXzIwMTEscG9wXzIwMjEscG9wX0NoYW5nZSxsYXllcikgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWxheWVyLCAtZ2VvbWV0cnkpICU+JQogIGdncGxvdCguLCBhZXMobGF5ZXIsIFZhbHVlLCBmaWxsPWxheWVyKSkgKyAKICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIpICsKICAgIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsKICAgIGxhYnModGl0bGU9Ik5ldyBEZXZlbG9wbWVudCBhcyBhIEZ1bmN0aW9uIG9mIEZhY3RvciBWYXJpYWJsZXMiKSArCiAgICBwbG90VGhlbWUKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCAlPiUKICBkcGx5cjo6c2VsZWN0KGxheWVyOm90aGVyVW5kZXZlbG9wZWQsZGV2ZWxvcGVkKSAlPiUKICBnYXRoZXIoTGFuZF9Db3Zlcl9UeXBlLCBWYWx1ZSwgLWxheWVyLCAtZ2VvbWV0cnkpICU+JQogICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgICAgZ3JvdXBfYnkobGF5ZXIsIExhbmRfQ292ZXJfVHlwZSkgJT4lCiAgICAgc3VtbWFyaXplKG4gPSBzdW0oYXMubnVtZXJpYyhWYWx1ZSkpKSAlPiUKICAgICB1bmdyb3VwKCkgJT4lCiAgICBtdXRhdGUoQ29udmVyc2lvbl9SYXRlID0gcGFzdGUwKHJvdW5kKDEwMCAqIG4vc3VtKG4pLCAyKSwgIiUiKSkgJT4lCiAgICBmaWx0ZXIobGF5ZXIgPT0gMSkgJT4lCiAgZHBseXI6OnNlbGVjdChMYW5kX0NvdmVyX1R5cGUsQ29udmVyc2lvbl9SYXRlKSAlPiUKICBrYWJsZSgpICU+JSBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGKQpgYGAKCiMgOTogUHJlZGljdGluZyBmb3IgMjAyMSBNb2RlbApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnNldC5zZWVkKDM0NTYpCnRyYWluSW5kZXggPC0gCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihkYXQkZGV2ZWxvcGVkLCBwID0gLjUwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSAxKQpkYXRUcmFpbiA8LSBkYXRbIHRyYWluSW5kZXgsXQpkYXRUZXN0ICA8LSBkYXRbLXRyYWluSW5kZXgsXQoKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9Ck1vZGVsMSA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCwgCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKQoKTW9kZWwyIDwtIGdsbShsYXllciB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQsIAogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikKICAgICAgICAgICAgICAKTW9kZWwzIDwtIGdsbShsYXllciB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBwb3BfMjAxMSwgCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAKICAgICAgICAgICAgICAKTW9kZWw0IDwtIGdsbShsYXllciB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBwb3BfMjAxMSArIAogICAgICAgICAgICAgIHBvcF8yMDIxLCAKICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAgICAgICAgICAgICAKICAgICAgICAgICAgCk1vZGVsNSA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wX0NoYW5nZSwgCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCk1vZGVsNiA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wX0NoYW5nZSArIAogICAgICAgICAgICAgIGRpc3RhbmNlX2hpZ2h3YXlzLCAKICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pIApgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cz0naGlkZSd9Cm1vZGVsTGlzdCA8LSBwYXN0ZTAoIk1vZGVsIiwgMTo2KQptYXBfZGZjKG1vZGVsTGlzdCwgZnVuY3Rpb24oeClwUjIoZ2V0KHgpKSlbNCxdICU+JQogIHNldE5hbWVzKHBhc3RlMCgiTW9kZWwiLDE6NikpICU+JQogIGdhdGhlcihNb2RlbCxNY0ZhZGRlbikgJT4lCiAgZ2dwbG90KGFlcyhNb2RlbCxNY0ZhZGRlbikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogICAgbGFicyh0aXRsZT0gIk1jRmFkZGVuIFItU3F1YXJlZCBieSBNb2RlbCIpICsKICAgIHBsb3RUaGVtZQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KdGVzdFNldFByb2JzIDwtIAogIGRhdGEuZnJhbWUoY2xhc3MgPSBkYXRUZXN0JGxheWVyLAogICAgICAgICAgICAgcHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0VGVzdCwgdHlwZT0icmVzcG9uc2UiKSkgCiAgCmdncGxvdCh0ZXN0U2V0UHJvYnMsIGFlcyhwcm9icykpICsKICBnZW9tX2RlbnNpdHkoYWVzKGZpbGw9Y2xhc3MpLCBhbHBoYT0wLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IikpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiB0ZXN0IHNldCBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyIsCiAgICAgICB4PSJQcmVkaWN0ZWQgUHJvYmFiaWxpdGllcyIseT0iRGVuc2l0eSIpICsKICBwbG90VGhlbWUKYGBgCgojIyA5LjE6IEFjY3VyYWN5CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kb3B0aW9ucyh5YXJkc3RpY2suZXZlbnRfZmlyc3QgPSBGQUxTRSkKCnRlc3RTZXRQcm9icyA8LSAKICB0ZXN0U2V0UHJvYnMgJT4lIAogIG11dGF0ZShwcmVkQ2xhc3NfMDUgPSBhcy5mYWN0b3IoaWZlbHNlKHRlc3RTZXRQcm9icyRwcm9icyA+PSAwLjA1ICwxLDApKSwKICAgICAgICAgcHJlZENsYXNzXzE3ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPj0gMC4xNyAsMSwwKSkpIAoKdGVzdFNldFByb2JzICU+JQogIGRwbHlyOjpzZWxlY3QoLXByb2JzKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtY2xhc3MpICU+JQogIGdyb3VwX2J5KFZhcmlhYmxlKSAlPiUKICBzdW1tYXJpemUoU2Vuc2l0aXZpdHkgPSByb3VuZCh5YXJkc3RpY2s6OnNlbnNfdmVjKGNsYXNzLGZhY3RvcihWYWx1ZSkpLDIpLAogICAgICAgICAgICBTcGVjaWZpY2l0eSA9IHJvdW5kKHlhcmRzdGljazo6c3BlY192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksCiAgICAgICAgICAgIEFjY3VyYWN5ID0gcm91bmQoeWFyZHN0aWNrOjphY2N1cmFjeV92ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMikpICU+JSAKICBrYWJsZSgpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpwcmVkc0Zvck1hcCA8LSAgICAgICAgIAogIGRhdCAlPiUKICAgIG11dGF0ZShwcm9icyA9IHByZWRpY3QoTW9kZWw2LCBkYXQsIHR5cGU9InJlc3BvbnNlIikgLAogICAgICAgICAgIFRocmVzaG9sZF81X1BjdCA9IGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4wNSAsMSwwKSksCiAgICAgICAgICAgVGhyZXNob2xkXzE3X1BjdCA9ICBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMTcgLDEsMCkpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QobGF5ZXIsVGhyZXNob2xkXzVfUGN0LFRocmVzaG9sZF8xN19QY3QpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtZ2VvbWV0cnkpICU+JQogICAgc3RfY2FzdCgiUE9MWUdPTiIpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGg9IDh9CmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGE9cHJlZHNGb3JNYXAsIGFlcyh4PXh5QyhwcmVkc0Zvck1hcClbLDFdLCB5PXh5QyhwcmVkc0Zvck1hcClbLDJdLCBjb2xvdXI9VmFsdWUpKSArCiAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLCBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBMb3cgVGhyZXNob2xkIikgKyAKICBtYXBUaGVtZQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KQ29uZnVzaW9uTWF0cml4Lm1ldHJpY3MgPC0KICBkYXQgJT4lCiAgICBtdXRhdGUocHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0LCB0eXBlPSJyZXNwb25zZSIpICwKICAgICAgICAgICBUaHJlc2hvbGRfNV9QY3QgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMDUgLDEsMCkpLAogICAgICAgICAgIFRocmVzaG9sZF8xN19QY3QgPSAgYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjE3ICwxLDApKSkgJT4lCiAgICBtdXRhdGUoVHJ1ZVBfMDUgPSBpZmVsc2UobGF5ZXIgID09IDEgJiBUaHJlc2hvbGRfNV9QY3QgPT0gMSwgMSwwKSwKICAgICAgICAgICBUcnVlTl8wNSA9IGlmZWxzZShsYXllciAgPT0gMCAmIFRocmVzaG9sZF81X1BjdCA9PSAwLCAxLDApLAogICAgICAgICAgIFRydWVQXzE3ID0gaWZlbHNlKGxheWVyICA9PSAxICYgVGhyZXNob2xkXzE3X1BjdCA9PSAxLCAxLDApLAogICAgICAgICAgIFRydWVOXzE3ID0gaWZlbHNlKGxheWVyICA9PSAwICYgVGhyZXNob2xkXzE3X1BjdCA9PSAwLCAxLDApKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLiwgc3RhcnRzX3dpdGgoIlRydWUiKSkgJT4lCiAgICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZ2VvbWV0cnkpICU+JQogICAgc3RfY2FzdCgiUE9MWUdPTiIpIApgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSA4IH0KZ2dwbG90KGRhdGE9Q29uZnVzaW9uTWF0cml4Lm1ldHJpY3MpICsKICBnZW9tX3BvaW50KGFlcyh4PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDFdLCAKICAgICAgICAgICAgICAgICB5PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDJdLCBjb2xvdXIgPSBhcy5mYWN0b3IoVmFsdWUpKSkgKwogIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIkNvcnJlY3QiLCJJbmNvcnJlY3QiKSwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBMb3cgVGhyZXNob2xkIikgKyBtYXBUaGVtZQpgYGAKCgojIDEwOiBQcmVkaWN0aW5nIExhbmQgQ292ZXIgRGVtYW5kIGZvciAyMDMxCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0IDwtCiAgZGF0ICU+JQogIG11dGF0ZShsYWdEZXZlbG9wbWVudCA9IG5uX2Z1bmN0aW9uKHh5QyguKSwgeHlDKGZpbHRlciguLGRldmVsb3BlZDEwID09IDEpKSwyKSkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiNDSEFOR0UgVEhJUyBDT0RFCmNvdW50eVBvcHVsYXRpb25fMjAzMSA8LSAKICBkYXRhLmZyYW1lKAogICBOQU1FID0gCiAgICAgYygiUGhpbGFkZWxwaGlhIiwiQ2hlc3RlciIsIk1vbnRnb21lcnkiLCJCdWNrcyIsIkRlbGF3YXJlIiksCiAgIGNvdW50eV9wcm9qZWN0aW9uXzIwMzEgPSAKICAgICBjKDE2NDM5NzEsIDU5OTkzMiwgODg0Mzg3LCA2NjkyOTksIDU3NzI0OCkpICU+JQogICNDSEFOR0UgTElORSBBQk9WRSAtIGZyb20gYSBzdHVkeSBEVlJQQyBwb3B1bGF0aW9uIHByb2plY3Rpb24KICAgbGVmdF9qb2luKAogICAgIGRhdCAlPiUKICAgICAgIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUKICAgICAgIGdyb3VwX2J5KE5BTUUpICU+JQogICAgICAgc3VtbWFyaXplKGNvdW50eV9wb3B1bGF0aW9uXzIwMjEgPSByb3VuZChzdW0ocG9wXzIwMjEpKSkpCgpjb3VudHlQb3B1bGF0aW9uXzIwMzEgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtTkFNRSkgJT4lCiAgZ2dwbG90KGFlcyhyZW9yZGVyKE5BTUUsLVZhbHVlKSxWYWx1ZSkpICsKICBnZW9tX2JhcihhZXMoZmlsbD1WYXJpYWJsZSksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMjAyMSIsIjIwMzEiKSwKICAgICAgICAgICAgICAgICAgICBuYW1lPSJQb3B1bGF0aW9uIikgKwogIGxhYnModGl0bGU9IlBvcHVsYXRpb24gQ2hhbmdlIGJ5IENvdW50eTogMjAyMSAtIDIwMzEiLAogICAgICAgeD0iQ291bnR5IiwgeT0iUG9wdWxhdGlvbiIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgcGxvdFRoZW1lCmBgYAoKIyMgMTAuMTogUHJlZGljdGluZyBEZXZlbG9wbWVudCBEZW1hbmQKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIHNlY3Rpb24gZWRpdGVkCmRhdF9pbmZpbGwgPC0KICBkYXQgJT4lCiAgI2NhbGN1bGF0ZSBwb3B1bGF0aW9uIGNoYW5nZQogICAgbGVmdF9qb2luKGNvdW50eVBvcHVsYXRpb25fMjAzMSkgJT4lCiAgICBtdXRhdGUocHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wID0gcG9wXzIwMjEgLyBjb3VudHlfcG9wdWxhdGlvbl8yMDIxLAogICAgICAgICAgIHBvcF8yMDMxLmluZmlsbCA9IHByb3BvcnRpb25fb2ZfY291bnR5X3BvcCAqIGNvdW50eV9wcm9qZWN0aW9uXzIwMzEsCiAgICAgICAgICAgcG9wX0NoYW5nZSA9IHJvdW5kKHBvcF8yMDMxLmluZmlsbCAtIHBvcF8yMDIxKSwyKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLWNvdW50eV9wcm9qZWN0aW9uXzIwMzEsIC1jb3VudHlfcG9wdWxhdGlvbl8yMDIxLCAKICAgICAgICAgICAgICAgICAgLXByb3BvcnRpb25fb2ZfY291bnR5X3BvcCwgLXBvcF8yMDMxLmluZmlsbCkgJT4lCiAgI3ByZWRpY3QgZm9yIDIwMzEKICAgIG11dGF0ZShwcmVkaWN0XzIwMzEuaW5maWxsID0gcHJlZGljdChNb2RlbDYsLiAsIHR5cGU9InJlc3BvbnNlIikpCgpkYXRfaW5maWxsICU+JQogIGdncGxvdCgpICsgIAogIGdlb21fcG9pbnQoYWVzKHg9eHlDKGRhdF9pbmZpbGwpWywxXSwgeT14eUMoZGF0X2luZmlsbClbLDJdLCBjb2xvdXIgPSBmYWN0b3IobnRpbGUocHJlZGljdF8yMDMxLmluZmlsbCw1KSkpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGRhdF9pbmZpbGwsInByZWRpY3RfMjAzMS5pbmZpbGwiKSwxLDQpLAogICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArCiAgZ2VvbV9zZihkYXRhPXN0dWR5QXJlYUNvdW50aWVzLCBmaWxsPU5BLCBjb2xvdXI9ImJsYWNrIiwgc2l6ZT0xKSArCiAgbGFicyh0aXRsZT0gIkRldmVsb3BtZW50IERlbWFuZCBpbiAyMDMxOiBQcmVkaWN0ZWQgUHJvYmFiaWxpdGllcyIpICsKICBtYXBUaGVtZQpgYGAKCiMgMTE6IENvbXBhcmluZyBQcmVkaWN0ZWQgRGV2ZWxvcG1lbnQgRGVtYW5kICYgRW52aXJvbm1lbnRhbCBTZW5zaXRpdml0eQojIyAxMS4yOiAyMDIxIExhbmQgQ292ZXIgRGF0YQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRldmVsb3BlZDIxIDwtIGxjXzIwMjEgPT0gMjEgfCBsY18yMDIxID09IDIyIHwgbGNfMjAyMSA9PSAyMyB8IGxjXzIwMjEgPT0gMjQKZm9yZXN0MjEgPC0gbGNfMjAyMSA9PSA0MSB8IGxjXzIwMjEgPT0gNDIgfCBsY18yMDIxID09IDQzIApmYXJtMjEgPC0gbGNfMjAyMSA9PSA4MSB8IGxjXzIwMjEgPT0gODIgCndldGxhbmRzMjEgPC0gbGNfMjAyMSA9PSA5MCB8IGxjXzIwMjEgPT0gOTUgCm90aGVyVW5kZXZlbG9wZWQyMSA8LSBsY18yMDIxID09IDUyIHwgbGNfMjAyMSA9PSA3MSB8IGxjXzIwMjEgPT0gMzEgCndhdGVyMjEgPC0gbGNfMjAyMSA9PSAxMQoKbmFtZXMoZGV2ZWxvcGVkMjEpIDwtICJkZXZlbG9wZWQyMSIKbmFtZXMoZm9yZXN0MjEpIDwtICJmb3Jlc3QyMSIKbmFtZXMoZmFybTIxKSA8LSAiZmFybTIxIgpuYW1lcyh3ZXRsYW5kczIxKSA8LSAid2V0bGFuZHMyMSIKbmFtZXMob3RoZXJVbmRldmVsb3BlZDIxKSA8LSAib3RoZXJVbmRldmVsb3BlZDIxIgpuYW1lcyh3YXRlcjIxKSA8LSAid2F0ZXIyMSIKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9ZHZNU0EpICsKICBnZW9tX3Jhc3RlcihkYXRhID0gcmJpbmQocmFzdChsY18yMDExKSAlPiUgbXV0YXRlKGxhYmVsID0gIjIwMTEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFzdChsY18yMDIxKSAlPiUgbXV0YXRlKGxhYmVsID0gIjIwMjEiKSkgJT4lIAogICAgICAgICAgICAgIG5hLm9taXQgJT4lIGZpbHRlcih2YWx1ZSA+IDApLCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBmYWNldF93cmFwKH5sYWJlbCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1UUlVFLCBuYW1lID0iIikgKwogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciwgMjAxMSAmIDIwMjEiKSArCiAgbWFwVGhlbWUgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQp0aGVSYXN0ZXJMaXN0MjEgPC0gYyhkZXZlbG9wZWQyMSxmb3Jlc3QyMSxmYXJtMjEsd2V0bGFuZHMyMSxvdGhlclVuZGV2ZWxvcGVkMjEsd2F0ZXIyMSkKCmRhdDIgPC0KICBhZ2dyZWdhdGVSYXN0ZXIodGhlUmFzdGVyTGlzdDIxLCBkYXQpICU+JQogIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkMjEsZm9yZXN0MjEsZmFybTIxLHdldGxhbmRzMjEsb3RoZXJVbmRldmVsb3BlZDIxLHdhdGVyMjEpICU+JQogIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUKICBiaW5kX2NvbHMoLixkYXQpICU+JQogIHN0X3NmKCkgJT4lCiAgc3RfY2FzdCgiUE9MWUdPTiIpCgpkYXQyICU+JQogIGdhdGhlcih2YXIsdmFsdWUsZGV2ZWxvcGVkMjE6d2F0ZXIyMSkgJT4lCiAgc3RfY2VudHJvaWQoKSAlPiUKICBtdXRhdGUoWCA9IHN0X2Nvb3JkaW5hdGVzKC4pWywxXSwKICAgICAgICAgWSA9IHN0X2Nvb3JkaW5hdGVzKC4pWywyXSkgJT4lCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9zZihkYXRhPWR2TVNBKSArCiAgICBnZW9tX3BvaW50KGFlcyhYLFksIGNvbG91cj1hcy5mYWN0b3IodmFsdWUpKSkgKwogICAgZmFjZXRfd3JhcCh+dmFyKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiT3RoZXIiLCJMYW5kIENvdmVyIiksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogICAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIFR5cGVzLCAyMDIxIiwKICAgICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArCiAgIG1hcFRoZW1lCmBgYAoKIyMgMTEuMzogU2Vuc2l0aXZlIExhbmQgQ292ZXIgTG9zdAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXQyIDwtCiAgZGF0MiAlPiUKICAgbXV0YXRlKHNlbnNpdGl2ZV9sb3N0MjEgPSBpZmVsc2UoZm9yZXN0ID09IDEgJiBmb3Jlc3QyMSA9PSAwIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2V0bGFuZHMgPT0gMSAmIHdldGxhbmRzMjEgPT0gMCwxLDApKQogICAgICAgICAgICAgICAgICAgICAgCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGE9ZGF0MiwgYWVzKHg9eHlDKGRhdDIpWywxXSwgeT14eUMoZGF0MilbLDJdLCBjb2xvdXI9YXMuZmFjdG9yKHNlbnNpdGl2ZV9sb3N0MjEpKSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiU2Vuc2l0aXZlIExvc3QiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogIGxhYnModGl0bGUgPSAiU2Vuc2l0aXZlIGxhbmRzIGxvc3Q6IDIwMTEgLSAyMDIxIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMTEuNDogTGFuZHNjYXBlIEZyYWdtZW50YXRpb24KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aD0gNn0Kc2Vuc2l0aXZlUmVnaW9ucyA8LSAKICByYXN0ZXI6OmNsdW1wKHdldGxhbmRzMjEgKyBmb3Jlc3QyMSkgJT4lCiAgcmFzdGVyVG9Qb2x5Z29ucygpICU+JQogIHN0X2FzX3NmKCkgJT4lCiAgZ3JvdXBfYnkoY2x1bXBzKSAlPiUgCiAgc3VtbWFyaXplKCkgJT4lCiAgICBtdXRhdGUoQWNyZXMgPSBhcy5udW1lcmljKHN0X2FyZWEoLikgKiAwLjAwMDAyMjk1NjgpKSAlPiUKICAgIGZpbHRlcihBY3JlcyA+IDM5NTQpICAlPiUKICBkcGx5cjo6c2VsZWN0KCkgJT4lCiAgcmFzdGVyOjpyYXN0ZXJpemUoLixlbXB0eVJhc3RlcikgCnNlbnNpdGl2ZVJlZ2lvbnNbc2Vuc2l0aXZlUmVnaW9ucyA+IDBdIDwtIDEgIApuYW1lcyhzZW5zaXRpdmVSZWdpb25zKSA8LSAic2Vuc2l0aXZlUmVnaW9ucyIKCmRhdDIgPC0KICBhZ2dyZWdhdGVSYXN0ZXIoYyhzZW5zaXRpdmVSZWdpb25zKSwgZGF0MikgJT4lCiAgZHBseXI6OnNlbGVjdChzZW5zaXRpdmVSZWdpb25zKSAlPiUKICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgYmluZF9jb2xzKC4sZGF0MikgJT4lCiAgc3Rfc2YoKQoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1kYXQyLCBhZXMoeD14eUMoZGF0MilbLDFdLCB5PXh5QyhkYXQyKVssMl0sIGNvbG91cj1hcy5mYWN0b3Ioc2Vuc2l0aXZlUmVnaW9ucykpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIlNlbnNpdGl2ZSBSZWdpb25zIiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZSA9ICJTZW5zaXRpdmUgcmVnaW9ucyIsCiAgICAgICBzdWJ0aXRsZSA9ICJDb250aW5vdXMgYXJlYXMgb2YgZWl0aGVyIHdldGxhbmRzIG9yIGZvcmVzdHNcbmdyZWF0ZXIgdGhhbiAxIGFjcmUiKSArCiAgbWFwVGhlbWUKYGBgCgojIyAxMS41OiBTdW1tYXJpemUgYnkgQ291bnR5CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KY291bnR5X3NwZWNpZmljX21ldHJpY3MgPC0gCiAgZGF0MiAlPiUKICAjcHJlZGljdCBkZXZlbG9wbWVudCBkZW1hbmQgZnJvbSBvdXIgbW9kZWwKICBtdXRhdGUoRGV2ZWxvcG1lbnRfRGVtYW5kID0gcHJlZGljdChNb2RlbDYsIGRhdDIsIHR5cGU9InJlc3BvbnNlIikpICU+JQogICNnZXQgYSBjb3VudCBjb3VudCBvZiBncmlkIGNlbGxzIGJ5IGNvdW50eSB3aGljaCB3ZSBjYW4gdXNlIHRvIGNhbGN1bGF0ZSByYXRlcyBiZWxvdwogIGxlZnRfam9pbihzdF9zZXRfZ2VvbWV0cnkoZGF0LCBOVUxMKSAlPiUgZ3JvdXBfYnkoTkFNRSkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkpICU+JQogICNjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIGJ5IGNvdW50eQogIGdyb3VwX2J5KE5BTUUpICU+JQogIHN1bW1hcml6ZShUb3RhbF9GYXJtbGFuZCA9IHN1bShmYXJtMjEpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgVG90YWxfRm9yZXN0ID0gc3VtKGZvcmVzdDIxKSAvIG1heChjb3VudCksCiAgICAgICAgICAgIFRvdGFsX1dldGxhbmRzID0gc3VtKHdldGxhbmRzMjEpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgVG90YWxfVW5kZXZlbG9wZWQgPSBzdW0ob3RoZXJVbmRldmVsb3BlZDIxKSAvIG1heChjb3VudCksCiAgICAgICAgICAgIFNlbnNpdGl2ZV9MYW5kX0xvc3QgPSBzdW0oc2Vuc2l0aXZlX2xvc3QyMSkgLyBtYXgoY291bnQpLAogICAgICAgICAgICBTZW5zaXRpdmVfUmVnaW9ucyA9IHN1bShzZW5zaXRpdmVSZWdpb25zKSAvIG1heChjb3VudCksCiAgICAgICAgICAgIE1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kID0gbWVhbihEZXZlbG9wbWVudF9EZW1hbmQpKSAlPiUKICAjZ2V0IHBvcHVsYXRpb24gZGF0YSBieSBjb3VudHkKICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDMxICU+JSAKICAgICAgICAgICAgbXV0YXRlKFBvcHVsYXRpb25fQ2hhbmdlID0gY291bnR5X3Byb2plY3Rpb25fMjAzMSAtIGNvdW50eV9wb3B1bGF0aW9uXzIwMjEsCiAgICAgICAgICAgICAgICAgICBQb3B1bGF0aW9uX0NoYW5nZV9SYXRlID0gUG9wdWxhdGlvbl9DaGFuZ2UgLyBjb3VudHlfcHJvamVjdGlvbl8yMDMxKSAlPiUKICAgICAgICAgICAgZHBseXI6OnNlbGVjdChOQU1FLFBvcHVsYXRpb25fQ2hhbmdlX1JhdGUpKSAlPiUKICBuYS5vbWl0KCkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmNvdW50eV9zcGVjaWZpY19tZXRyaWNzICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1OQU1FLCAtZ2VvbWV0cnkpICU+JQogIG11dGF0ZShWYXJpYWJsZSA9IGZhY3RvcihWYXJpYWJsZSwgbGV2ZWxzPWMoIlBvcHVsYXRpb25fQ2hhbmdlX1JhdGUiLCJNZWFuX0RldmVsb3BtZW50X0RlbWFuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfRmFybWxhbmQiLCJUb3RhbF9VbmRldmVsb3BlZCIsIlRvdGFsX0ZvcmVzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfV2V0bGFuZHMiLCJTZW5zaXRpdmVfTGFuZF9Mb3N0IiwiU2Vuc2l0aXZlX1JlZ2lvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpKSkgJT4lCiAgbXV0YXRlKFBsYW5uaW5nX0Rlc2lnbmF0aW9uID0gY2FzZV93aGVuKAogICAgVmFyaWFibGUgPT0gIlBvcHVsYXRpb25fQ2hhbmdlX1JhdGUiIHwgVmFyaWFibGUgPT0gIk1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kIiB+ICJEZW1hbmQtU2lkZSIsCiAgICBWYXJpYWJsZSA9PSAiVG90YWxfRmFybWxhbmQiIHwgVmFyaWFibGUgPT0gIlRvdGFsX1VuZGV2ZWxvcGVkIiAgICAgICAgICAgICAgIH4gIlN1aXRhYmxlIiwKICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiAiTm90IFN1aXRhYmxlIikpICU+JQogIGdncGxvdChhZXMoeD1WYXJpYWJsZSwgeT1WYWx1ZSwgZmlsbD1QbGFubmluZ19EZXNpZ25hdGlvbikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSwgY29sb3VyPSJibGFjayIpICsKICAgIGZhY2V0X3dyYXAofk5BTUUsIG5jb2w9NSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoLjI1LCAxLCBieSA9IC4yNSkpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIuNSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LjUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIsImRhcmtncmVlbiIpKSArCiAgICBsYWJzKHRpdGxlPSAiQ291bnR5IFNwZWNpZmljIEFsbG9jYXRpb24gTWV0cmljcyIsIHN1YnRpdGxlPSAiQXMgcmF0ZXMiLCB4PSJJbmRpY2F0b3IiLCB5PSJSYXRlIikgKwogICAgcGxvdFRoZW1lICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpgYGAKCiMgMTI6IFNjZW5hcmlvIDE6IEFsbG9jYXRpb24KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0PSA4LCBmaWcud2lkdGg9IDExfQpjaGVzdGVyIDwtCiAgZGF0MiAlPiUKICAgIG11dGF0ZShEZXZlbG9wbWVudF9EZW1hbmQgPSBwcmVkaWN0KE1vZGVsNiwgZGF0MiwgdHlwZT0icmVzcG9uc2UiKSkgJT4lCiAgICBmaWx0ZXIoTkFNRSA9PSAiQ2hlc3RlciIpIAoKY2hlc3Rlcl9sYW5kVXNlIDwtIHJiaW5kKAogIGZpbHRlcihjaGVzdGVyLCBmb3Jlc3QyMSA9PSAxIHwgd2V0bGFuZHMyMSA9PSAxICkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiTm90IFN1aXRhYmxlIiksCiAgZmlsdGVyKGNoZXN0ZXIsIGRldmVsb3BlZDIxID09IDEpICU+JQogIGRwbHlyOjpzZWxlY3QoKSAlPiUgbXV0YXRlKExhbmRfVXNlID0gIkRldmVsb3BlZCIpKQoKZ3JpZC5hcnJhbmdlKApnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPWNoZXN0ZXIsIGFlcyhmaWxsPWZhY3RvcihudGlsZShEZXZlbG9wbWVudF9EZW1hbmQsNSkpKSwgY29sb3VyPU5BKSArCiAgZ2VvbV9wb2ludChkYXRhPWNoZXN0ZXJfbGFuZFVzZSwgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNSwgc2l6ZSA9IDIpICsKICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKGR2SGlnaHdheXMsZmlsdGVyKHN0dWR5QXJlYUNvdW50aWVzLCBOQU1FPT0iQ2hlc3RlciIpKSwgc2l6ZT0yKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IkRldmVsb3BtZW50XG5EZW1hbmQiLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoY2hlc3RlciwiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IFBvdGVudGlhbCwgMjAzMTogQ2hlc3RlciIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Y2hlc3RlciwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF9DaGFuZ2UsNSkpKSwgY29sb3VyPU5BKSArCiAgZ2VvbV9wb2ludChkYXRhPWNoZXN0ZXJfbGFuZFVzZSwgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNSwgc2l6ZSA9IDIpICsKICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKGR2SGlnaHdheXMsZmlsdGVyKHN0dWR5QXJlYUNvdW50aWVzLCBOQU1FPT0iQ2hlc3RlciIpKSwgc2l6ZT0yKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlBvcHVsYXRpb25cbkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhjaGVzdGVyLCJwb3BfQ2hhbmdlIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCAyMDMxOiBDaGVzdGVyIikgKyBtYXBUaGVtZSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpLCBuY29sPTIpCmBgYAoKIyAxMzogU2NlbmFyaW8gMiBUcmFuc3BvcnRhdGlvbgojIyAxMy4xOiBIaWdod2F5IERpc3RhbmNlCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KI2R2SGlnaHdheXMgPC0KIyAgc3RfcmVhZCgiQzovVXNlcnMvZmVybmEvT25lRHJpdmUvRG9jdW1lbnRzL0FyY0dJUy9Qcm9qZWN0cy9DUExOIDY3NTAvSFc1L2R2X3JvYWRzLmdlb2pzb24iKSAlPiUKIyAgc3RfdHJhbnNmb3JtKHN0X2Nycyhkdk1TQSkpICU+JQojICBzdF9pbnRlcnNlY3Rpb24oZHZNU0EpCgpkdlRyYW5zaXQgPC0KICBzdF9yZWFkKCIvVXNlcnMvbHV5aWl3b25nL0RvY3VtZW50cy9HaXRIdWIvTGFuZFVzZU1vZGVsaW5nX0hXNS9EZWxhd2FyZVZhbGxleS9kdl90cmFuc2l0Lmdlb2pzb24iKSAlPiUKICBzdF90cmFuc2Zvcm0oc3RfY3JzKGR2TVNBKSkgJT4lCiAgc3RfaW50ZXJzZWN0aW9uKGR2TVNBKQpgYGAKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0V9CmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGE9ZmlzaG5ldCwgCiAgICAgICAgICAgICBhZXMoeD14eUMoZmlzaG5ldClbLDFdLCB5PXh5QyhmaXNobmV0KVssMl0sY29sb3VyPWxheWVyKSxzaXplPTEuNSkgKwogIGdlb21fc2YoZGF0YT1kdlRyYW5zaXQpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5gdyBEZXZlbG9wbWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgYW5kIE5ldyBUcmFuc2l0IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmVtcHR5UmFzdGVyIDwtIGxjX2NoYW5nZQplbXB0eVJhc3RlcltdIDwtIE5BCgpkdlRyYW5zaXRfc3BkZiA8LSBhcyhkdlRyYW5zaXQsICJTcGF0aWFsIikKdHJhbnNpdF9yYXN0ZXIgPC0gcmFzdGVyaXplKGR2VHJhbnNpdCwgZW1wdHlSYXN0ZXIpCgojaGlnaHdheV9yYXN0ZXIgPC0gCiAgI2FzKGR2VHJhbnNpdCwnU3BhdGlhbCcpICU+JQogICNyYXN0ZXJpemUoLixlbXB0eVJhc3RlcikKCnRyYW5zaXRfcmFzdGVyX2Rpc3RhbmNlIDwtIGRpc3RhbmNlKHRyYW5zaXRfcmFzdGVyKQpuYW1lcyh0cmFuc2l0X3Jhc3Rlcl9kaXN0YW5jZSkgPC0gImRpc3RhbmNlX3RyYW5zaXQiCgp0cmFuc2l0UG9pbnRzIDwtCiAgcmFzdGVyVG9Qb2ludHModHJhbnNpdF9yYXN0ZXJfZGlzdGFuY2UpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKGR2TVNBX2Zpc2huZXQpKQoKdHJhbnNpdFBvaW50c19maXNobmV0IDwtIAogIGFnZ3JlZ2F0ZSh0cmFuc2l0UG9pbnRzLCBkdk1TQV9maXNobmV0LCBtZWFuKSAlPiUKICBtdXRhdGUoZGlzdGFuY2VfdHJhbnNpdCA9IGlmZWxzZShpcy5uYShkaXN0YW5jZV90cmFuc2l0KSwwLGRpc3RhbmNlX3RyYW5zaXQpKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT10cmFuc2l0UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5Qyh0cmFuc2l0UG9pbnRzX2Zpc2huZXQpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKHRyYW5zaXRQb2ludHNfZmlzaG5ldClbLDJdLCAKICAgICAgICAgICAgICAgICBjb2xvdXI9ZmFjdG9yKG50aWxlKGRpc3RhbmNlX3RyYW5zaXQsNSkpKSxzaXplPTEuNSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKHRyYW5zaXRQb2ludHNfZmlzaG5ldCwiZGlzdGFuY2VfdHJhbnNpdCIpLDEsOCksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZS9uQnJlYWtzIikgKwogIGdlb21fc2YoZGF0YT1kdlRyYW5zaXQsIGNvbG91ciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byB0cmFuc2l0IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzOyB0cmFuc2l0IHZpc3VhbGl6ZWQgaW4gcmVkIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMTMuMjogQ3JlYXRlIGEgbmV3IGRhdGFzZXQgZm9yIHRoZSBtb2RlbApjaGFuZ2luZyB0aGUgZGlzdGFuY2UgZnJvbSBoaWdod2F5IHRvIGRpc3RhbmNlIHRvIHRyYW5zaXQKYGBge3J9CmRhdC4yIDwtIAogIGNiaW5kKGZpc2huZXQsIHRyYW5zaXRQb2ludHNfZmlzaG5ldCwgZmlzaG5ldFBvcHVsYXRpb24sIGFnZ3JlZ2F0ZWRSYXN0ZXJzKSU+JQogIGRwbHlyOjpzZWxlY3QobGF5ZXIsIGRldmVsb3BlZCwgZm9yZXN0LCBmYXJtLCB3ZXRsYW5kcywgb3RoZXJVbmRldmVsb3BlZCwgd2F0ZXIsCiAgICAgICAgICAgICAgICBwb3BfMjAxMSwgcG9wXzIwMjEsIHBvcF9DaGFuZ2UsIGRpc3RhbmNlX3RyYW5zaXQsbGFnRGV2ZWxvcG1lbnQpICU+JQogIHN0X2pvaW4oc3R1ZHlBcmVhQ291bnRpZXMpICU+JQogIG11dGF0ZShkZXZlbG9wZWQxMCA9IGlmZWxzZShsYXllciA9PSAxICYgZGV2ZWxvcGVkID09IDEsIDAsIGRldmVsb3BlZCkpICU+JQogIGZpbHRlcih3YXRlciA9PSAwKSAKYGBgCgojIyAxMy4zOiBDcmVhdGUgdHJhaW5pbmcgc2V0CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc2V0LnNlZWQoMzQ1NikKdHJhaW5JbmRleCA8LSAKICBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdCRkZXZlbG9wZWQsIHAgPSAuNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IDEpCnRyYW5zaXQuZGF0VHJhaW4gPC0gZGF0LjJbIHRyYWluSW5kZXgsXQp0cmFuc2l0LmRhdFRlc3QgIDwtIGRhdC4yWy10cmFpbkluZGV4LF0KCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIHRlc3QgbW9kZWwgd2l0aCB0cmFuc2l0CnRyYW5zaXRNb2RlbCA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wX0NoYW5nZSArIAogICAgICAgICAgICAgIGRpc3RhbmNlX3RyYW5zaXQsIAogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSB0cmFuc2l0LmRhdFRyYWluKSAKc3VtbWFyeSh0cmFuc2l0TW9kZWwpCiN2YXJpYWJsZXMgYXJlIGhpZ2hseSBzaWduaWZpY2FudAojci1zcXVhcmUgdmFsdWU6IDE0LjglCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQp0ZXN0U2V0UHJvYnMuMiA8LSAKICBkYXRhLmZyYW1lKGNsYXNzID0gZGF0VGVzdCRsYXllciwKICAgICAgICAgICAgIHByb2JzID0gcHJlZGljdCh0cmFuc2l0TW9kZWwsIHRyYW5zaXQuZGF0VGVzdCwgdHlwZT0icmVzcG9uc2UiKSkgCiAgCmdncGxvdCh0ZXN0U2V0UHJvYnMuMiwgYWVzKHByb2JzKSkgKwogIGdlb21fZGVuc2l0eShhZXMoZmlsbD1jbGFzcyksIGFscGhhPTAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSkgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIiwKICAgICAgIHg9IlByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIix5PSJEZW5zaXR5IikgKwogIHBsb3RUaGVtZQpgYGAKCiMjIDEzLjQ6IENyZWF0aW5nIG5ldyBkYXRhZnJhbWUgZm9yIHBvcCBwcm9qZWN0aW9uCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0Lm5ldyA8LQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0MjEsIGRhdC4yKSAlPiUKICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZDIxLGZvcmVzdDIxLGZhcm0yMSx3ZXRsYW5kczIxLG90aGVyVW5kZXZlbG9wZWQyMSx3YXRlcjIxKSAlPiUKICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgYmluZF9jb2xzKC4sZGF0LjIpICU+JQogIHN0X3NmKCkgJT4lCiAgc3RfY2FzdCgiUE9MWUdPTiIpCgojIGNvbnNpZGVyaW5nIGxhbmQgZnJhZ21lbnRhdGlvbgpkYXQubmV3IDwtCiAgYWdncmVnYXRlUmFzdGVyKGMoc2Vuc2l0aXZlUmVnaW9ucyksIGRhdC5uZXcpICU+JQogIGRwbHlyOjpzZWxlY3Qoc2Vuc2l0aXZlUmVnaW9ucykgJT4lCiAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQogIGJpbmRfY29scyguLGRhdC4yKSAlPiUKICBzdF9zZigpCgpgYGAKCiMjIDEzLjU6IExhbmQgYWxsb2NhdGlvbiAtIHBvcHVsYXRpb24gcHJvamVjdGlvbiBhbmQgZGV2ZWxvcG1lbnQgZm9yY2FzdApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQ9IDgsIGZpZy53aWR0aD0gMTF9CmNoZXN0ZXIubmV3IDwtCiAgZGF0Lm5ldyAlPiUKICAgIG11dGF0ZShEZXZlbG9wbWVudF9EZW1hbmQgPSBwcmVkaWN0KHRyYW5zaXRNb2RlbCwgZGF0Lm5ldywgdHlwZT0icmVzcG9uc2UiKSkgJT4lCiAgICBmaWx0ZXIoTkFNRSA9PSAiQ2hlc3RlciIpIAoKY2hlc3Rlcl9sYW5kVXNlLm5ldyA8LSByYmluZCgKICBmaWx0ZXIoY2hlc3RlciwgZm9yZXN0MjEgPT0gMSB8IHdldGxhbmRzMjEgPT0gMSApICU+JQogIGRwbHlyOjpzZWxlY3QoKSAlPiUgbXV0YXRlKExhbmRfVXNlID0gIk5vdCBTdWl0YWJsZSIpLAogIGZpbHRlcihjaGVzdGVyLCBkZXZlbG9wZWQyMSA9PSAxKSAlPiUKICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJEZXZlbG9wZWQiKSkKCmdyaWQuYXJyYW5nZSgKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1jaGVzdGVyLm5ldywgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKERldmVsb3BtZW50X0RlbWFuZCw1KSkpLCBjb2xvdXI9TkEpICsKICBnZW9tX3BvaW50KGRhdGE9Y2hlc3Rlcl9sYW5kVXNlLm5ldywgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZS5uZXcpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhjaGVzdGVyX2xhbmRVc2UubmV3KVssMl0sIGNvbG91cj1MYW5kX1VzZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gMikgKwogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oZHZIaWdod2F5cyxmaWx0ZXIoc3R1ZHlBcmVhQ291bnRpZXMsIE5BTUU9PSJDaGVzdGVyIikpLCBzaXplPTIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iRGV2ZWxvcG1lbnRcbkRlbWFuZCIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhjaGVzdGVyLm5ldywiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IFBvdGVudGlhbCwgMjAzMTogQ2hlc3RlciIsCiAgICAgICBzdWJ0aXRsZSA9ICJBZGRpdGlvbmFsIFRyYW5zaXQiKSArIG1hcFRoZW1lICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLCBjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSksCgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPWNoZXN0ZXIubmV3LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wX0NoYW5nZSw1KSkpLCBjb2xvdXI9TkEpICsKICBnZW9tX3BvaW50KGRhdGE9Y2hlc3Rlcl9sYW5kVXNlLm5ldywgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZS5uZXcpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhjaGVzdGVyX2xhbmRVc2UubmV3KVssMl0sIGNvbG91cj1MYW5kX1VzZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gMikgKwogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oZHZIaWdod2F5cyxmaWx0ZXIoc3R1ZHlBcmVhQ291bnRpZXMsIE5BTUU9PSJDaGVzdGVyIikpLCBzaXplPTIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iUG9wdWxhdGlvblxuQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGNoZXN0ZXIubmV3LCJwb3BfQ2hhbmdlIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCAyMDMxOiBDaGVzdGVyIiwKICAgICAgIHN1YnRpdGxlID0gIkFkZGl0aW9uYWwgVHJhbnNpdCIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwgbmNvbD0yKQpgYGA=